代码之家  ›  专栏  ›  技术社区  ›  Chris Farmer Marcelo Cantos

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

  •  2
  • Chris Farmer Marcelo Cantos  · 技术社区  · 15 年前

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

    I need what's in the red rectangle http://img168.imageshack.us/img168/8169/5142010110014am.png

    我有一个可以工作的实现,但是它依赖于外部应用程序的用户界面,这对我来说是不保证的,所以我更愿意找到更一般的东西。

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

    所以,首先,是否可以可靠地保存窗口内容的位图?如果是这样,最好的方法是什么?我的 WMX打印 具有 SendMessage 尝试?

    2 回复  |  直到 15 年前
        1
  •  4
  •   Chris Farmer Marcelo Cantos    15 年前

    本次修改 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");
    

    如果应用程序被另一个窗口覆盖,这会起作用,但如果应用程序最小化,它就不起作用。我想我可以忍受。

        2
  •  1
  •   JSBÕ±Õ¸Õ£Õ¹    15 年前

    没有 可靠的 如果某个应用程序不在顶部,则可以从另一个应用程序获取位图。这是因为如果应用程序不可见,应用程序的控件甚至不会呈现,而且Windows也不一定记得在控件失去z顺序中的最顶端位置之后,控件的最后已知内容是什么。

    你最好的选择是在你需要截屏的时候将目标应用程序移动到z顺序的前面,然后在捕获图像后有选择地恢复原来的z顺序。