如何在没有使用NativeMethods的情况下,根据给定的hWnd查找窗口的位置/位置?

18

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

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问题,其中讨论了这个问题,但他们的答案是使用本机方法。

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

3个回答

40

我最近在一个项目中遇到了这个问题,但是无法找到任何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);

谢谢!看起来那样就可以了。 - Robert P
你好,Robert,你能否发布你的完整解决方案?我想为Firefox和其他浏览器(如Chrome)添加屏幕截图功能。提前感谢! Jeroen(WatiN首席开发人员) - Jeroen van Menen
完整的解决方案在这里:https://dev59.com/_XM_5IYBdhLWcg3wThN3#1461322 - Robert P

6
不行 - 如果你没有创建表单,你必须使用P/Invoke GetWindowRect。我认为没有托管等效项。

4
答案与其他人所说的一样,很可能是“不行,你不能使用非本机方法截取hwnd中的随机窗口。” 在我展示它之前,请注意以下几点:
预先警告:
对于任何想要使用此代码的人,请注意从VisibleClipBounds获得的大小仅在窗口内部,并且不包括边框或标题栏。 它是可绘制区域。 如果您有这个,您也许可以在没有p/invoke的情况下完成这个任务。
如果您能计算浏览器窗口的边框,您可以使用VisibleClipBounds。如果需要,您可以使用SystemInformation对象获取重要信息,例如{{link2: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;
    }   
}

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