17

我目前正在使用 WatiN,并发现它是一个很棒的网页浏览自动化工具。但是,截至上一个版本,它的屏幕捕获功能似乎有所欠缺。除了Charles Petzold 的一些代码之外,我还提出了一个可行的解决方案,用于从屏幕上捕获屏幕截图(独立生成类似于此 StackOverflow 问题的代码) 。不幸的是,缺少一个组件:实际的窗口在哪里

WatiN 方便地为hWnd您提供浏览器,因此我们可以(通过这个简化的示例)设置为从屏幕复制图像,如下所示:

// browser is either an WatiN.Core.IE or a WatiN.Core.FireFox...
IntPtr hWnd = browser.hWnd;
string filename = "my_file.bmp";
using (Graphics browser = Graphics.FromHwnd(browser.hWnd) )
using (Bitmap screenshot = new Bitmap((int)browser.VisibleClipBounds.Width,
                                      (int)browser.VisibleClipBounds.Height,
                                      browser))
using (Graphics screenGraphics = Graphics.FromImage(screenshot))
{
    int hWndX = 0; // Upper left of graphics?  Nope, 
    int hWndY = 0; // this is upper left of the entire desktop!

    screenGraphics.CopyFromScreen(hWndX, hWndY, 0, 0, 
                          new Size((int)browser.VisibileClipBounds.Width,
                                   (int)browser.VisibileClipBounds.Height));
    screenshot.Save(filename, ImageFormat.Bmp);
}

成功!我们得到了屏幕截图,但有一个问题:总是指向屏幕的最左上角,而不是我们要从中复制的窗口的位置 hWndXhWndY

然后我调查了Control.FromHandle,但这似乎只适用于您创建的表单;如果您将 传递给该方法,则此方法将返回一个空指针hWnd

然后,进一步阅读导致我切换搜索条件......当大多数人真正想要窗口的“位置”时,我一直在搜索“窗口位置”。这导致另一个 SO question谈到了这一点,但他们的答案是使用本机方法。

那么,只有给定 hWnd (最好只有 .NET 2.0 时代的库),是否有一种本地 C# 方法来查找窗口的位置?

4

3 回答 3

37

我刚刚在一个项目中经历了这个,但找不到任何托管的 C# 方式。

要添加到 Reed 的答案,P/Invoke 代码是:

 [DllImport("user32.dll", SetLastError = true)]
 [return: MarshalAs(UnmanagedType.Bool)]
 static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
 [StructLayout(LayoutKind.Sequential)]
 private struct RECT
 {
     public int Left;
     public int Top;
     public int Right;
     public int Bottom;
  }

称它为:

  RECT rct = new RECT();
  GetWindowRect(hWnd, ref rct);
于 2009-09-16T18:03:46.583 回答
6

不 - 如果您没有创建表单,则必须 P/Invoke GetWindowRect。我不相信有一个托管的等价物。

于 2009-09-16T17:54:07.440 回答
4

答案就像其他人所说的那样,可能是“不,如果没有本机方法,您不能从 hwnd 中截取随机窗口的屏幕截图。”。在我展示之前有几个警告:

预警:

对于想要使用此代码的任何人,请注意 VisibleClipBounds 给出的大小仅窗口内,包括边框或标题栏。这是可绘制区域。如果你有这个,你也许可以在没有 p/invoke 的情况下做到这一点。

(如果您可以计算浏览器窗口的边框,您可以使用 VisibleClipBounds。如果您愿意,您可以使用该对象SystemInformation获取重要信息,例如标题栏的高度,但这听起来像是虫子的黑魔法。)Border3DSize

这相当于窗口的Ctrl+Printscreen。这也没有 WatiN 屏幕截图功能所做的精细操作,例如滚动浏览器并拍摄整个页面的图像。这适合我的项目,但可能不适合您的项目。

增强功能:

如果您在 .NET 3 和陆地上,这可以更改为扩展方法,并且可以很容易地添加图像类型的选项(我ImageFormat.Bmp在此示例中默认为)。

代码:

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

public class Screenshot
{
    class NativeMethods
    {
        // http://msdn.microsoft.com/en-us/library/ms633519(VS.85).aspx
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        // http://msdn.microsoft.com/en-us/library/a5ch4fda(VS.80).aspx
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
    }
    /// <summary>
    /// Takes a screenshot of the browser.
    /// </summary>
    /// <param name="b">The browser object.</param>
    /// <param name="filename">The path to store the file.</param>
    /// <returns></returns>
    public static bool SaveScreenshot(Browser b, string filename)
    {
        bool success = false;
        IntPtr hWnd = b.hWnd;
        NativeMethods.RECT rect = new NativeMethods.RECT();
        if (NativeMethods.GetWindowRect(hWnd, ref rect))
        {
            Size size = new Size(rect.Right - rect.Left,
                                 rect.Bottom - rect.Top);
            // Get information about the screen
            using (Graphics browserGraphics = Graphics.FromHwnd(hWnd))
            // apply that info to a bitmap...
            using (Bitmap screenshot = new Bitmap(size.Width, size.Height, 
                                                  browserGraphics))
            // and create an Graphics to manipulate that bitmap.
            using (Graphics imageGraphics = Graphics.FromImage(screenshot))
            {
                int hWndX = rect.Left;
                int hWndY = rect.Top;
                imageGraphics.CopyFromScreen(hWndX, hWndY, 0, 0, size);
                screenshot.Save(filename, ImageFormat.Bmp);
                success = true;
            }
        }
        // otherwise, fails.
        return success;
    }   
}
于 2009-09-22T17:05:27.107 回答