如何在没有NativeMethods的情况下找到hWnd窗口的位置/位置?

时间:2009-09-16 17:51:41

标签: c# .net winapi watin screenshot

我目前正在与WatiN合作,并发现它是一款出色的网页浏览自动化工具。但是,截至上一版本,它的屏幕捕获功能似乎缺乏。除了一些this StackOverflow question之外,我还提出了一个可行的解决方案,用于从屏幕捕获屏幕截图(独立生成类似于code by 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传递给它,则此方法返回空指针。

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

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

3 个答案:

答案 0 :(得分:33)

我刚刚在一个项目中完成了这个,但无法找到任何托管的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);

答案 1 :(得分:6)

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

答案 2 :(得分:4)

答案正如其他人所说的那样,可能是“不,你不能在没有本地方法的情况下从hwnd中截取随机窗口的截图。”在我展示之前几点警告:

<强>预警:

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

(如果您可以计算浏览器窗口的边框,可以使用VisibleClipBounds。如果您愿意,可以使用SystemInformation对象获取Border3DSize等重要信息,或者您可以尝试通过创建一个虚拟形式和deriving the border and title bar height from that计算它,但所有听起来都像是由错误组成的黑魔法。)

这相当于窗口的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;
    }   
}