通过句柄捕获窗口屏幕

4
我正在尝试捕获桌面上的特定窗口,但我得到了一个混合图像,一部分是窗口,一部分是桌面区域。请问我漏掉了什么?
以下是我的代码:
RECT rect = new RECT();

if (!SetForegroundWindow(handle))
    throw new Win32Exception(Marshal.GetLastWin32Error());

if (!GetWindowRect(handle, out rect))
    throw new Win32Exception(Marshal.GetLastWin32Error());

Thread.Sleep(500);

Rectangle windowSize = rect.ToRectangle();
Bitmap target = new Bitmap(windowSize.Width, windowSize.Height);
using (Graphics g = Graphics.FromImage(target))
{
    g.CopyFromScreen(0, 0, 0, 0, new Size(windowSize.Width, windowSize.Height));
}

target.Save("foo.png", System.Drawing.Imaging.ImageFormat.Png);

水晶球说你忘记 声明你的应用程序dpiAware了。 - Hans Passant
2个回答

12

我认为你代码中的问题出在这行:

g.CopyFromScreen(0, 0, 0, 0, new Size(windowSize.Width, windowSize.Height));

它应该是:

g.CopyFromScreen(windowSize.X, windowSize.Y, 0, 0, new Size(windowSize.Width, windowSize.Height));

这是我个人用来获取特定窗口图像的方法 - 这可能会很有用:

public Bitmap GetScreenshot()
{
    IntPtr hwnd = ihandle;//handle here

    RECT rc;
    Win32.GetWindowRect(new HandleRef(null, hwnd), out rc);

    Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
    Graphics gfxBmp = Graphics.FromImage(bmp);
    IntPtr hdcBitmap;
    try
    {
        hdcBitmap = gfxBmp.GetHdc();
    }
    catch
    {
        return null;
    }
    bool succeeded = Win32.PrintWindow(hwnd, hdcBitmap, 0);
    gfxBmp.ReleaseHdc(hdcBitmap);
    if (!succeeded)
    {
        gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
    }
    IntPtr hRgn = Win32.CreateRectRgn(0, 0, 0, 0);
    Win32.GetWindowRgn(hwnd, hRgn);
    Region region = Region.FromHrgn(hRgn);//err here once
    if (!region.IsEmpty(gfxBmp))
    {
        gfxBmp.ExcludeClip(region);
        gfxBmp.Clear(Color.Transparent);
    }
    gfxBmp.Dispose();
    return bmp;
 }

谢谢,这是一个更好的方法。 - Jack
一旦我可以再次点赞,我会立即给你点赞的...今天点赞太多了哈哈 - Jack
好的代码!不过你应该添加 Win32.DeleteObject(hRgn)。还有,当返回 null 时,bmpgfxBmp 应该被释放。 - Paul B.

11
我正在改进PixelZerg的答案,该答案不包括必要的使用和外部引用...
要使用下面的代码,只需创建一个ScreenCapture类实例,并将C# Winforms生成窗口的句柄传递给GetScreenshot例程。这将返回一个位图对象。要将其写入JPG文件,请将位图对象传递给WriteBitmapToFile方法:
添加一个using语句,引用你的ScreenCapture类:
using StackOverflow.Example.Utility;

现在实例化ScreenCapture并按照以下方式使用:

var sc = new ScreenCapture();
var bitmap = sc.GetScreenshot(parentContainer.Handle);
sc.WriteBitmapToFile("temp.jpg", bitmap);

这是ScreenCapture类的完整可编译代码:

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace StackOverflow.Example.Utility
{

    public class ScreenCapture
    {
        [DllImport("user32.dll")]
        static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);

        //Region Flags - The return value specifies the type of the region that the function obtains. It can be one of the following values.
        const int ERROR = 0;
        const int NULLREGION = 1;
        const int SIMPLEREGION = 2;
        const int COMPLEXREGION = 3;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

        [DllImport("gdi32.dll")]
        static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left, Top, Right, Bottom;

            public RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }

            public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }

            public int X
            {
                get { return Left; }
                set { Right -= (Left - value); Left = value; }
            }

            public int Y
            {
                get { return Top; }
                set { Bottom -= (Top - value); Top = value; }
            }

            public int Height
            {
                get { return Bottom - Top; }
                set { Bottom = value + Top; }
            }

            public int Width
            {
                get { return Right - Left; }
                set { Right = value + Left; }
            }

            public System.Drawing.Point Location
            {
                get { return new System.Drawing.Point(Left, Top); }
                set { X = value.X; Y = value.Y; }
            }

            public System.Drawing.Size Size
            {
                get { return new System.Drawing.Size(Width, Height); }
                set { Width = value.Width; Height = value.Height; }
            }

            public static implicit operator System.Drawing.Rectangle(RECT r)
            {
                return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
            }

            public static implicit operator RECT(System.Drawing.Rectangle r)
            {
                return new RECT(r);
            }

            public static bool operator ==(RECT r1, RECT r2)
            {
                return r1.Equals(r2);
            }

            public static bool operator !=(RECT r1, RECT r2)
            {
                return !r1.Equals(r2);
            }

            public bool Equals(RECT r)
            {
                return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
            }

            public override bool Equals(object obj)
            {
                if (obj is RECT)
                    return Equals((RECT)obj);
                else if (obj is System.Drawing.Rectangle)
                    return Equals(new RECT((System.Drawing.Rectangle)obj));
                return false;
            }

            public override int GetHashCode()
            {
                return ((System.Drawing.Rectangle)this).GetHashCode();
            }

            public override string ToString()
            {
                return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
            }
        }
        public Bitmap GetScreenshot(IntPtr ihandle)
        {
            IntPtr hwnd = ihandle;//handle here

            RECT rc;
            GetWindowRect(new HandleRef(null, hwnd), out rc);

            Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
            Graphics gfxBmp = Graphics.FromImage(bmp);
            IntPtr hdcBitmap;
            try
            {
                hdcBitmap = gfxBmp.GetHdc();
            }
            catch
            {
                return null;
            }
            bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
            gfxBmp.ReleaseHdc(hdcBitmap);
            if (!succeeded)
            {
                gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
            }
            IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
            GetWindowRgn(hwnd, hRgn);
            Region region = Region.FromHrgn(hRgn);//err here once
            if (!region.IsEmpty(gfxBmp))
            {
                gfxBmp.ExcludeClip(region);
                gfxBmp.Clear(Color.Transparent);
            }
            gfxBmp.Dispose();
            return bmp;
        }

        public void WriteBitmapToFile(string filename, Bitmap bitmap)
        {
            bitmap.Save(filename, ImageFormat.Jpeg);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接