4

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

我需要红色矩形中的内容

我有一个可行的实现,但它取决于外部应用程序的 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消息发送到窗口的建议。几个月前的这个问题/答案似乎很有希望,但是当我使用这段代码时,我只得到一个白色矩形,没有我的控件的实际内容。

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_PRINTSendMessage尝试有什么问题?

4

2 回答 2

6

pinvoke.net 网站上对PrintWindowAPI 示例的这种修改似乎起到了作用。

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");

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

于 2010-05-14T18:41:42.227 回答
1

There is no reliable way to get a bitmap from a different app if that app is not on top. That's because the app's controls do not even render if the app isn't visible, and Windows does not necessarily even remember what the last known contents of the control were after that control loses the topmost position in the z-order.

Your best bet is to move the target app to the front of the z-order at the time that you need to take the screenshot, and then optionally restore the original z-order after capturing the image.

于 2010-05-14T16:26:11.453 回答