Can I get a bitmap of an arbitrary window in another application process?
Posted
by Chris Farmer
on Stack Overflow
See other posts from Stack Overflow
or by Chris Farmer
Published on 2010-05-14T16:08:35Z
Indexed on
2010/05/14
18:44 UTC
Read the original article
Hit count: 307
I am trying to automate a third-party Win32 application where I want to capture the graphics content of a particular window at defined time intervals. I am in the early phases of this, and I'm currently trying to use the Microsoft UI Automation API via C# to do most of the interaction between my client app and the external app. I can now get the external app to do what I want it to do, but now I want to capture the graphics from a specific window that seems to be some third-party owner-drawn control. How can I do this? The window I want to capture is the one marked by the red rectangle in this image:
I have an implementation that sort of works, but it's dependent on the external app's UI being on top, and that's not guaranteed for me, so I'd prefer to 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 automated app is on top, but it just blindly copies the screen in the rectangle, so my code is at the mercy of whatever happens to be running on the machine and might cover my app's window.
I have read some suggestions to send the WM_PRINT
message to the window. This question/answer from a few months back seemed promising, but when I use this code, I just get a white rectangle with none of my control's actual contents.
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 even possible for me to reliably save a bitmap of a window's contents? If so, what is the best way, and what is wrong with my WM_PRINT
with SendMessage
attempt?
© Stack Overflow or respective owner