gdi+ - 我可以在另一个应用程序进程中获取任意窗口的位图吗?

标签 gdi+ sendmessage microsoft-ui-automation

我正在尝试自动化第三方 Win32 应用程序,我想在定义的时间间隔捕获特定窗口的图形内容。我正处于早期阶段,我目前正在尝试使用 Microsoft UI Automation API 通过 C# 完成我的客户端应用程序和外部应用程序之间的大部分交互。我现在可以让外部应用程序执行我想要它执行的操作,但现在我想从似乎是第三方所有者绘制的控件的特定窗口中捕获图形。我怎样才能做到这一点?我要捕获的窗口是此图像中红色矩形标记的窗口:

I need what's in the red rectangle

我有一个这样的实现,但它依赖于外部应用程序的 UI 位于顶部,这对我来说并不能保证,所以我更愿意找到更通用的东西。

var p = Process.Start("c:\myapp.exe");
var mainForm = AutomationElement.FromHandle(p.MainWindowHandle);
// "workspace" below is the window whose content I want to capture.
var workspace = mainForm.FindFirst(TreeScope.Descendents,
                    new PropertyCondition(AutomationElement.ClassNameProperty, "AfxFrameOrView70u"));
var rect = (Rect) workspace.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
using (var bmp = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen((int)rect.Left, (int)rect.Top, 0, 0, new Size((int)rect.Width, (int)rect.Height));
        bmp.Save(@"c:\screenshot.png", ImageFormat.Png);
    }
}

当自动化应用程序在顶部时,上面的工作足够好,但它只是盲目地复制矩形中的屏幕,所以我的代码受机器上运行的任何东西的支配,并且可能覆盖我的应用程序的窗口。

我已经阅读了一些建议发送 WM_PRINT消息到窗口。此 question/answer几个月前看起来很有希望,但是当我使用此代码时,我只得到一个白色矩形,其中没有控件的实际内容。
var prop = (int)workspace.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
var hwnd = new IntPtr(prop);
using ( var bmp2 = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (Graphics g = Graphics.FromImage(bmp2))
    {
        g.FillRectangle(SystemBrushes.Control, 0, 0, (int)rect.Width, (int)rect.Height);
        try
        {
            SendMessage(hwnd, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
        }
        finally
        {
            g.ReleaseHdc();
        }
        bmp2.Save(@"c:\screenshot.bmp");
    }
}

那么,首先,我是否有可能可靠地保存窗口内容的位图?如果是这样,最好的方法是什么,我的 WM_PRINT 有什么问题?与 SendMessage试图?

最佳答案

PrintWindow API example的这个修改在 pinvoke.net 网站上似乎已经做到了。

Bitmap bmp = new Bitmap((int)rect.Width, (int)rect.Height);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
bool success = PrintWindow(hwnd, dc, 0);
memoryGraphics.ReleaseHdc(dc);
bmp.Save(@"c:\screenshot.bmp");

如果应用程序被另一个窗口覆盖,则此方法有效,但如果应用程序已最小化,则此方法无效。我想我可以忍受。

关于gdi+ - 我可以在另一个应用程序进程中获取任意窗口的位图吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2835656/

相关文章:

.net - 矩阵变换点与变换向量

asp.net-mvc - GDI+ 中发生一般性错误

c# - 将缓存与 AutomationElements 一起使用不会给我加速 - 预期或错误的用法?

delphi - 将 SafeArray 从 Delphi 传递到 ms-uiautomation 库

.net - 如何根据字符串的大小自动调整图像的大小?

.net - GDI+ 用这个 "generic error"害死我了

c# - 如何使用 sendmessage 和 c# 增加第三方应用程序中轨迹栏/ slider 的值(value)?

c++ - 如何将击键发送到其他进程(例如记事本)?

delphi - 如何以编程方式选择另一个应用程序中的上下文菜单项?

c# - 是否可以使用 Microsoft Automation UI 在 WPF/WinForm App 上单击标签控件