none
为什么在待机状态下,截取的屏幕是黑色的,如何解决? RRS feed

  • 问题

  • 在待机状态下,通过c#编程得到的截取屏幕的图像是黑色的,但是通过键盘上的印屏幕系统请求键(PrtSc Sys Rq)截取的屏幕却是待机图像。二者在原理上有什么差别?如何通过c#编程也得到可用的图像,不通过模拟按PrtSc键实现?
    2011年3月8日 2:07

答案

  • 我个人建议,最偷懒的办法就是用程序模拟触发PrtSc按钮事件,然后到剪贴板你连把图片取出来就OK了!!

    再者说了,待机的时候其实返回什么都不重要了,因为这个时候不是正在使用的业务屏幕。

    哦当然,除非对方电脑的待机画面比较诱人。。。HoHo~~~


    信奎爷,无所畏惧!!
    2011年3月8日 10:03
  • 2011年3月9日 1:38
    版主
  • 你好

    我用下面的代码测试了你所说的问题,我也进入了待机界面,但是似乎一切正常。你试试。

     public partial class Form1 : Form
     {
      List<PictureBox> lis = new List<PictureBox>();
      public const int SM_CXSCREEN = 0;
      public const int SM_CYSCREEN = 1;
      public const int SRCCOPY = 13369376;
      public struct SIZE
      {
       public int cx;
       public int cy;
      }
    
      [DllImport("user32.dll", SetLastError = false)]
      static extern IntPtr GetDesktopWindow();
      [DllImport("user32.dll")]
      static extern IntPtr GetWindowDC(IntPtr hWnd);
      [DllImport("gdi32.dll")]
      static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
      [DllImport("gdi32.dll", SetLastError = true)]
      static extern IntPtr CreateCompatibleDC(IntPtr hdc);
      [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
      static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
      [DllImport("User32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
      [DllImport("gdi32.dll")]
      static extern bool DeleteDC(IntPtr hdc);
      [DllImport("user32.dll", EntryPoint = "GetDC")]
      public static extern IntPtr GetDC(IntPtr ptr);
      [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
      public static extern int GetSystemMetrics(int abc);
      [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
      public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
      [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
      public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);
      [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
      public static extern IntPtr DeleteObject(IntPtr hDc);
      IntPtr hwn = GetDesktopWindow();
      Bitmap bt = null;
      public static Bitmap GetDesktopImage()
      {
       SIZE size;
       IntPtr hBitmap;
       IntPtr hDC = GetDC(GetDesktopWindow());
       IntPtr hMemDC = CreateCompatibleDC(hDC);
       size.cx = GetSystemMetrics(SM_CXSCREEN);
       size.cy = GetSystemMetrics(SM_CYSCREEN);
       hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
       if (hBitmap != IntPtr.Zero)
       {
        IntPtr hOld = (IntPtr)SelectObject(hMemDC, hBitmap);
        BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
        SelectObject(hMemDC, hOld);
        DeleteDC(hMemDC);
        ReleaseDC(GetDesktopWindow(), hDC);
        Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);
        DeleteObject(hBitmap);
        GC.Collect();
        return bmp;
       }
       return null;
      }
      public PictureBox this[int index]
      {
       get
       {
        return lis[index];
       }
      }
       
      public Form1()
      {
       InitializeComponent();
       lis.Add(this.pictureBox1);
       lis.Add(this.pictureBox2);
       lis.Add(this.pictureBox3);
       lis.Add(this.pictureBox4);
       lis.Add(this.pictureBox5);
       lis.Add(this.pictureBox6);
       lis.Add(this.pictureBox7);
       lis.Add(this.pictureBox8);
       lis.Add(this.pictureBox9);
       lis.Add(this.pictureBox10);
       lis.Add(this.pictureBox11);
       lis.Add(this.pictureBox12);
       lis.Add(this.pictureBox13);
      }
      
      private void timer1_Tick(object sender, EventArgs e)
      {
       for (int i = 0; i < 13; i++)
       {
        this[i].Image = GetDesktopImage();
       }
       
      }  
     }
    

    对了,顺便说下,插入图片的方法:

    1.首先把你的图片上传到网上,这样就有一个图片的url(比如我这个图片 http://1bwsma.bay.livefilestore.com/y1p5V9fzkE4mELBSoEzcsb6E-oW-ZUytpNeJARiy64QwIYZuzBBcU6X8bRK--AEsz2DTUMSqpGZVVE4KehgAvmx0mz158iDysC_/formfortest.jpg?psid=1

    2.点击编辑框的HTML,在里面加入代码块<Image src="url"/>

    希望对你有所帮助。


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月10日 3:00
  •  抱歉,代码有点问题。

    把timer1_Tick方法改成如下,的确是黑色的图片。

     int x = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
          
          if (x == 5*13)
          {
            this.timer1.Enabled = false;
            return;
          }
          
          for (int i = 0; i < 13; i++)
          {
            this[i].Image = GetDesktopImage();
            x++;
          }
         
        }
    

     


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月10日 4:50
  • 这个是对它的介绍:

    我没有发现可以用编程来实现,这个是很底层的东西了,就像上面说的,是从DOS时期就存在的。况且C#对底层的操作本身就不是很好。

    建议你看看这篇文章,虽然是英文的,至少代码看的懂。教你怎么去获取一个PrintScreen keypress 然后保存到一个image里面。

    http://www.codeproject.com/KB/cs/PrintScreen.aspx

    祝你好运!


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月11日 3:44

全部回复

  • 你的具体代码?
    http://feiyun0112.cnblogs.com/
    2011年3月8日 2:13
    版主
  • 。。。这个还用贴代码啊。

    Size sz = new Size(dx,dy);//dx,dy是截取屏幕的一个小矩形
                IntPtr hDesk = GetDesktopWindow();
                IntPtr hSrce = GetWindowDC(hDesk);
                IntPtr hDest = CreateCompatibleDC(hSrce);
                IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
                IntPtr hOldBmp = SelectObject(hDest, hBmp);
                bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, x0,y0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);//这里的x0,y0是屏幕中的矩形的起始坐标
                Bitmap bmp = Bitmap.FromHbitmap(hBmp);//这里得到的图片显示出来,如果不是待机就是正常图像,如果是待机就是黑色的
                SelectObject(hDest, hOldBmp);
                DeleteObject(hBmp);
                DeleteDC(hDest);
                ReleaseDC(hDesk, hSrce);
    2011年3月8日 2:31
  • 我个人建议,最偷懒的办法就是用程序模拟触发PrtSc按钮事件,然后到剪贴板你连把图片取出来就OK了!!

    再者说了,待机的时候其实返回什么都不重要了,因为这个时候不是正在使用的业务屏幕。

    哦当然,除非对方电脑的待机画面比较诱人。。。HoHo~~~


    信奎爷,无所畏惧!!
    2011年3月8日 10:03
  • 2011年3月9日 1:38
    版主
  • 你好

    请试试这段代码

    public static Bitmap GetWindow (IntPtr hWnd) 
    { 
    IntPtr hscrdc = GetWindowDC (hWnd); 
    Control control = Control.FromHandle (hWnd); 
    IntPtr hbitmap = CreateCompatibleBitmap (hscrdc, control.Width, control.Height); 
    IntPtr hmemdc = CreateCompatibleDC (hscrdc); 
    SelectObject (hmemdc, hbitmap); 
    PrintWindow (hWnd, hmemdc, 0); 
    Bitmap bmp = Bitmap.FromHbitmap (hbitmap); 
    DeleteDC (hscrdc); / / delete the objects used 
    DeleteDC (hmemdc); / / delete the objects used 
    return bmp; 
    } 
    
    希望对你有所帮助。
    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月9日 6:56
  • 恩,在待机的时候,我如何得到待机界面的 hwnd,或者如何得到桌面的hWnd?

    这是的桌面的hwnd是不是无效的呢?有些资料显示,得到的桌面句柄无效的。

    而待机界面的hwnd根据windows的安全机制,能得到么?有些资料表明,程序无法得到待机界面是的textbox等句柄,这是安全性的保证。

    不知道上述是否属实,希望能够得到解释

    2011年3月9日 8:01
  • 有资料表明,频繁按在PrtSc键会导致死机,另外在实现功能上,剪贴板还有别的用处,这里就不能使用了。

    谢谢你的建议

    2011年3月9日 8:03
  • 你好

    花了点时间把代码完善了下。经过测试是可以截取桌面的。就是截取后显示在一个pictureBox上。

     public partial class Form1 : Form
      {
    
        public const int SM_CXSCREEN = 0;
        public const int SM_CYSCREEN = 1;
        public const int SRCCOPY = 13369376;
    
    
        [DllImport("user32.dll", SetLastError = false)]
        static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [DllImport("User32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
    
        [DllImport("gdi32.dll")]
        static extern bool DeleteDC(IntPtr hdc);
        [DllImport("user32.dll", EntryPoint = "GetDC")]
        public static extern IntPtr GetDC(IntPtr ptr);
        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        public static extern int GetSystemMetrics(int abc);
        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
        [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
        public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);
    
        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        public static extern IntPtr DeleteObject(IntPtr hDc);
    
        IntPtr hwn = GetDesktopWindow();
        Bitmap bt = null;
    
        public static Bitmap GetDesktopImage()
        {
          SIZE size;
          IntPtr hBitmap;
          IntPtr hDC = GetDC(GetDesktopWindow());
          IntPtr hMemDC = CreateCompatibleDC(hDC);
          size.cx = GetSystemMetrics(SM_CXSCREEN);
          size.cy = GetSystemMetrics(SM_CYSCREEN);
          hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
          if (hBitmap != IntPtr.Zero)
          {
            
            IntPtr hOld = (IntPtr)SelectObject(hMemDC, hBitmap);        
            BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);        
            SelectObject(hMemDC, hOld);        
            DeleteDC(hMemDC);       
            ReleaseDC(GetDesktopWindow(), hDC);        
            Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);        
            DeleteObject(hBitmap);
            GC.Collect();
            return bmp;
          }
          return null;
        }
     
        public Form1()
        {
          InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
          bt = GetDesktopImage();
          pictureBox1.Image = bt;
        }
    
      }
    

    你试试在待机状态下,截取的是什么样子。

    希望对你有所帮助!


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月9日 15:42
  • god,不能贴图。

    非常感谢Cookie Luo但还是不能完成截图。

    我验证设计的流程是这样的。 在winform中添加一个interval为5000的定时器,每次都截取屏幕贴在pictureBox上的某个部分上,注意,每次贴与上次贴不是重叠的位置。然后,这样,我“切换用户”,进入的待机界面,紧接着我又重新登录,这时候就可以看到在待机状态截取图片,发现确实是黑色的。

    大致图像用文字表示是   图图图图图黑黑黑图图图...  其中黑的地方就是待机时截图。。。

    是我的用法不对么?

    或者有些地方理解的不对呢?

    或者究竟是怎么回事?


    2011年3月10日 1:20
  • 你好

    我用下面的代码测试了你所说的问题,我也进入了待机界面,但是似乎一切正常。你试试。

     public partial class Form1 : Form
     {
      List<PictureBox> lis = new List<PictureBox>();
      public const int SM_CXSCREEN = 0;
      public const int SM_CYSCREEN = 1;
      public const int SRCCOPY = 13369376;
      public struct SIZE
      {
       public int cx;
       public int cy;
      }
    
      [DllImport("user32.dll", SetLastError = false)]
      static extern IntPtr GetDesktopWindow();
      [DllImport("user32.dll")]
      static extern IntPtr GetWindowDC(IntPtr hWnd);
      [DllImport("gdi32.dll")]
      static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
      [DllImport("gdi32.dll", SetLastError = true)]
      static extern IntPtr CreateCompatibleDC(IntPtr hdc);
      [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
      static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
      [DllImport("User32.dll", SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
      [DllImport("gdi32.dll")]
      static extern bool DeleteDC(IntPtr hdc);
      [DllImport("user32.dll", EntryPoint = "GetDC")]
      public static extern IntPtr GetDC(IntPtr ptr);
      [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
      public static extern int GetSystemMetrics(int abc);
      [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
      public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
      [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
      public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);
      [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
      public static extern IntPtr DeleteObject(IntPtr hDc);
      IntPtr hwn = GetDesktopWindow();
      Bitmap bt = null;
      public static Bitmap GetDesktopImage()
      {
       SIZE size;
       IntPtr hBitmap;
       IntPtr hDC = GetDC(GetDesktopWindow());
       IntPtr hMemDC = CreateCompatibleDC(hDC);
       size.cx = GetSystemMetrics(SM_CXSCREEN);
       size.cy = GetSystemMetrics(SM_CYSCREEN);
       hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
       if (hBitmap != IntPtr.Zero)
       {
        IntPtr hOld = (IntPtr)SelectObject(hMemDC, hBitmap);
        BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
        SelectObject(hMemDC, hOld);
        DeleteDC(hMemDC);
        ReleaseDC(GetDesktopWindow(), hDC);
        Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);
        DeleteObject(hBitmap);
        GC.Collect();
        return bmp;
       }
       return null;
      }
      public PictureBox this[int index]
      {
       get
       {
        return lis[index];
       }
      }
       
      public Form1()
      {
       InitializeComponent();
       lis.Add(this.pictureBox1);
       lis.Add(this.pictureBox2);
       lis.Add(this.pictureBox3);
       lis.Add(this.pictureBox4);
       lis.Add(this.pictureBox5);
       lis.Add(this.pictureBox6);
       lis.Add(this.pictureBox7);
       lis.Add(this.pictureBox8);
       lis.Add(this.pictureBox9);
       lis.Add(this.pictureBox10);
       lis.Add(this.pictureBox11);
       lis.Add(this.pictureBox12);
       lis.Add(this.pictureBox13);
      }
      
      private void timer1_Tick(object sender, EventArgs e)
      {
       for (int i = 0; i < 13; i++)
       {
        this[i].Image = GetDesktopImage();
       }
       
      }  
     }
    

    对了,顺便说下,插入图片的方法:

    1.首先把你的图片上传到网上,这样就有一个图片的url(比如我这个图片 http://1bwsma.bay.livefilestore.com/y1p5V9fzkE4mELBSoEzcsb6E-oW-ZUytpNeJARiy64QwIYZuzBBcU6X8bRK--AEsz2DTUMSqpGZVVE4KehgAvmx0mz158iDysC_/formfortest.jpg?psid=1

    2.点击编辑框的HTML,在里面加入代码块<Image src="url"/>

    希望对你有所帮助。


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月10日 3:00
  •  抱歉,代码有点问题。

    把timer1_Tick方法改成如下,的确是黑色的图片。

     int x = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
          
          if (x == 5*13)
          {
            this.timer1.Enabled = false;
            return;
          }
          
          for (int i = 0; i < 13; i++)
          {
            this[i].Image = GetDesktopImage();
            x++;
          }
         
        }
    

     


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月10日 4:50
  • 谢谢~

    还是没能达到目标.

    我把你的代码定时器触发时间部分改成如下的形式

    定时器间隔定为3秒

     

    int cnt = 0;

            private void timer1_Tick_1(object sender, EventArgs e)

            {

                if(cnt >= 13)cnt = 0;

                this[cnt].Image = GetDesktopImage();

                cnt++;            

            }

    然后运行后,等待6秒之后,切换用户操作,重新登录操作,看到的图片是这样的.

    其中的黑色部分就是切换用户时截取屏幕得到的图像.

     

     

    2011年3月10日 4:54
  • 哦~ 居然比我早.

    既然这样,原因是什么呢?如何解决呢?

    2011年3月10日 4:58
  • 这个问题似乎还没好的解决方案,建议你最好是正常情况下用PrintWindow,当进入待机的时候就印屏幕系统请求键去截取。减少频繁使用这个截取键。


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月11日 2:00
  • 似乎也只有这样子了,那么印屏幕系统请求这个原理是什么呢? 不能通过编程来实现么?
    2011年3月11日 3:16
  • 这个是对它的介绍:

    我没有发现可以用编程来实现,这个是很底层的东西了,就像上面说的,是从DOS时期就存在的。况且C#对底层的操作本身就不是很好。

    建议你看看这篇文章,虽然是英文的,至少代码看的懂。教你怎么去获取一个PrintScreen keypress 然后保存到一个image里面。

    http://www.codeproject.com/KB/cs/PrintScreen.aspx

    祝你好运!


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • 已标记为答案 msbyebye 2011年3月11日 7:34
    2011年3月11日 3:44
  • OK只能这样子了~ 呵呵,谢谢~
    2011年3月11日 7:34