Can I get a bitmap of an arbitrary window in another application process?

I am trying to automate a third party Win32 application in which I want to write the graphical content of a specific window at specific intervals. I'm in the early stages of doing this, and I'm currently trying to use the Microsoft UI Automation API via C # for most of the interaction between my client app and an external app. Now I can get the external application to do what I want it to do, but now I want to grab the graphics from a specific window that appears to be an owner-drawn third party control. How can i do this? The window that I want to capture is marked with a red rectangular in this image:

I need what's in the red rectangle

I have an implementation like this, but it depends on the frontend of an external application, and this is not guaranteed to me, so I would rather find something more general.

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

      

The above works well enough when the automatic app is on top, but it just blindly copies the screen in a rectangular shape, so my code depends on what's going on on the computer and can close my app window.

I have read several suggestions to send a message WM_PRINT

to the window. This question / answer , received a few months ago, seemed promising, but when I use this code, I just get a white rectangular nickname without any control over my actual content.

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

      

So first, is it possible for me to reliably store a bitmap of the window's contents? If so, what's the best way and what's wrong with my WM_PRINT

attempt SendMessage

?

+2


a source to share


2 answers


This modification of the PrintWindow

API
example at pinvoke.net seemed to do the trick.

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

      



This works if the application is closed by another window, but it does not work if the application is minimized. I think I can live with it.

+4


a source


There is no reliable way to get a bitmap from another application unless that application is on top. This is because the app's controls don't even render unless the app is visible, and Windows doesn't even necessarily remember what the last known content of a control was after that control lost its top-most position in z-order.



Your best bet is to move the Destination Application to the beginning of the z-order at the time you need to take a screenshot, and then restore the original z-order after capturing the image if necessary.

+1


a source







All Articles