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

发布于 2024-08-05 03:15:08 字数 1814 浏览 3 评论 0原文

我目前正在使用 WatiN,发现它是一个很棒的网络浏览自动化工具。然而,截至上一个版本,它的屏幕捕获功能似乎缺乏。我提出了一个可行的解决方案,用于从屏幕捕获屏幕截图(独立生成类似于 this 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问题 谈到了这一点,但他们的答案是使用本机方法。

那么,是否有一种原生 C# 方法可以仅在给定 hWnd 的情况下查找窗口的位置(最好仅使用 .NET 2.0 时代的库)?

I'm currently working with WatiN, and finding it to be a great web browsing automation tool. However, as of the last release, it's screen capturing functionality seems to be lacking. I've come up with a workable solution for capturing screenshots from the screen (independently generating code similar to this StackOverflow question) in addition to some code by Charles Petzold. Unfortunately, there is a missing component: Where is the actual window?

WatiN conveniently provides the browser's hWnd to you, so we can (with this simplified example) get set to copy an image from the screen, like so:

// 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);
}

Success! We get screenshots, but there's that problem: hWndX and hWndY always point to the upper left most corner of the screen, not the location of the window we want to copy from.

I then looked into Control.FromHandle, however this seems to only work with forms you created; this method returns a null pointer if you pass the hWnd into it.

Then, further reading lead me to switch my search criteria...I had been searching for 'location of window' when most people really want the 'position' of the window. This lead to another SO question that talked about this, but their answer was to use native methods.

So, Is there a native C# way of finding the position of a window, only given the hWnd (preferably with only .NET 2.0 era libraries)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心病无药医 2024-08-12 03:15:08

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

I just went through this on a project and was unable to find any managed C# way.

To add to Reed's answer the P/Invoke code is:

 [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;
  }

Call it as:

  RECT rct = new RECT();
  GetWindowRect(hWnd, ref rct);
简单气质女生网名 2024-08-12 03:15:08

否 - 如果您没有创建表单,则必须 P/Invoke GetWindowRect< /a>.我不相信有一个可管理的等价物。

No - if you didn't create the form, you have to P/Invoke GetWindowRect. I don't believe there is a managed equivalent.

月寒剑心 2024-08-12 03:15:08

答案正如其他人所说,可能是“不,如果没有本机方法,您无法从 hwnd 中截取随机窗口的屏幕截图。”。在展示之前,有几点需要注意:

警告:

对于任何想要使用此代码的人,请注意,VisibleClipBounds 给出的大小仅窗口内,并且< em>不包括边框或标题栏。这是可绘制区域。如果你有这个,你也许可以在没有 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;
    }   
}

The answer is as others have stated, probably "No, you cannot take a screenshot of a random window from an hwnd without native methods.". Couple of caveats before I show it:

Forewarning:

For anyone who wants to use this code, note that the size given from the VisibleClipBounds is only inside the window, and does not include the border or title bar. It's the drawable area. If you had that, you might be able to do this without p/invoke.

(If you could calculate the border of the browser window, you could use the VisibleClipBounds. If you wanted, you could use the SystemInformation object to get important info like Border3DSize, or you could try to calculate it by creating a dummy form and deriving the border and title bar height from that, but that all sounds like the black magic that bugs are made of.)

This is equivalent to Ctrl+Printscreen of the window. This also does not do the niceties that the WatiN screenshot capability does, such as scroll the browser and take an image of the whole page. This is suitable for my project, but may not be for yours.

Enhancements:

This could be changed to be an extension method if you're in .NET 3 and up-land, and an option for the image type could be added pretty easily (I default to ImageFormat.Bmp for this example).

Code:

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;
    }   
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文