【问题标题】:Saving a screenshot of a window using C#, WPF, and DWM使用 C#、WPF 和 DWM 保存窗口的屏幕截图
【发布时间】:2010-12-23 21:47:10
【问题描述】:

这是this question的后续问题

上述解决方案使用 DWM 显示活动窗口的缩略图。如果我理解正确,它的工作原理是让您指定要查看的应用程序的窗口句柄,然后让您提供窗口句柄和该窗口上的位置,窗口应在该位置绘制目标窗口的内容。

有没有办法将窗口屏幕截图直接渲染到 BitmapImage 或 Image,而不是直接在窗口中的某个位置绘制它? (基本上只是抓取窗口的屏幕截图——即使它被另一个窗口覆盖——而不使用更新缩略图。)

感谢您的帮助!

【问题讨论】:

    标签: c# wpf screenshot dwm


    【解决方案1】:

    Control.DrawToBitmap 并不总是有效,因此我求助于以下提供更一致结果的本机 API 调用:

    实用程序类。调用 Utilities.CaptureWindow(Control.Handle) 捕获特定控件:

    public static class Utilities
    {
        public static Image CaptureScreen()
        {
            return CaptureWindow(User32.GetDesktopWindow());
        }
    
        public static Image CaptureWindow(IntPtr handle)
        {
    
            IntPtr hdcSrc = User32.GetWindowDC(handle);
    
            RECT windowRect = new RECT();
            User32.GetWindowRect(handle, ref windowRect);
    
            int width = windowRect.right - windowRect.left;
            int height = windowRect.bottom - windowRect.top;
    
            IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
            IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height);
    
            IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
            Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, ApiConstants.SRCCOPY);
            Gdi32.SelectObject(hdcDest, hOld);
            Gdi32.DeleteDC(hdcDest);
            User32.ReleaseDC(handle, hdcSrc);
    
            Image image = Image.FromHbitmap(hBitmap);
            Gdi32.DeleteObject(hBitmap);
    
            return image;
        }
    }
    

    Gdi32 类:

    public class Gdi32
    {
        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
    }
    

    User32 类:

    public static class User32
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
        [DllImport("user32.dll")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
    }
    

    使用的常量:

        public const int SRCCOPY = 13369376;
    

    使用的结构:

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }
    

    一个友好的控件扩展方法:

    public static class ControlExtensions
    {
        public static Image DrawToImage(this Control control)
        {
            return Utilities.CaptureWindow(control.Handle);
        }
    }
    

    【讨论】:

    【解决方案2】:

    使用Control.DrawToBitmap Method

    Image img = myForm.DrawToBitmap();
    Image img = myPanel.DrawToBitmap();
    

    【讨论】:

    • 感谢您的回复。这不是需要先在屏幕上绘制屏幕截图然后保存吗?有没有办法将窗口的屏幕截图直接捕获到对象(如图像)上,而不必先在屏幕上绘制它?
    • DrawToBitmap 获得一个位图,就像您在按下 PrintScreen 按钮时所做的一样(仅在表单上或面板上)。我不确定表单或面板是否应该真正显示在屏幕上,可见面板肯定有效,我相信隐藏也应该有效。位图 bm = new Bitmap(wid, hgt); ' 将当前窗体绘制到位图 this.DrawToBitmap(bm, new Rectangle(0, 0, wid, hgt))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多