General capturing with WM_PRINT and WM_PRINTCLIENT

2019-05-30 10:05发布

So far I got this partially working capturing function in C#:

/// <summary>
/// Captures a HWND, using a WM_PRINT windows message to draw into a memory DC.
/// Pro: Does also work if  the HWND is not positioned inside the visible screen.
/// Con: Does only work if the target handle supports WM_PRINT and WM_PRINTCLIENT.
/// TODO: Subclass the target handle and hack the WM_PRINT support for various systems, including Windows 7.
///       Example for Windows 2000 see http://www.fengyuan.com/article/wmprint.html.
/// </summary>
/// <param name="hWnd">The HWND.</param>
/// <returns>Bitmap of the captured visual window, if the operation succeeds. Null, if the operation fails.</returns>
public static Bitmap CaptureHWnd_WM_PRINT(UIntPtr hWnd)
{
    Bitmap bitmap = null;
    UIntPtr hDC = User32.GetWindowDC(hWnd); //Get the device context of hWnd
    if (hDC != UIntPtr.Zero)
    {
        UIntPtr hMemDC = Gdi32.CreateCompatibleDC(hDC); //Create a memory device context, compatible with hDC
        if (hMemDC != UIntPtr.Zero)
        {
            RECT rc; MyGetWindowRect(hWnd, out rc); //Get bounds of hWnd
            UIntPtr hbitmap = Gdi32.CreateCompatibleBitmap(hDC, rc.Width, rc.Height); //Create a bitmap handle, compatible with hDC
            if (hbitmap != UIntPtr.Zero)
            {
                UIntPtr hOld = Gdi32.SelectObject(hMemDC, hbitmap); //Select hbitmap into hMemDC
                //#also tried: User32.SendMessage(hWnd, WM.PRINT, hMemDC, (UIntPtr)(DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_NONCLIENT | DrawingOptions.PRF_OWNED));
                User32.DefWindowProc(hWnd, WM.PRINT, hMemDC, (UIntPtr)(DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_NONCLIENT | DrawingOptions.PRF_OWNED));
                bitmap = Image.FromHbitmap(hbitmap.ToIntPtr()); //Create a managed Bitmap out of hbitmap
                Gdi32.SelectObject(hMemDC, hOld); //Select hOld into hMemDC (the previously replaced object), to leave hMemDC as found
                Gdi32.DeleteObject(hbitmap); //Free hbitmap
            }
            Gdi32.DeleteDC(hMemDC); //Free hdcMem
        }
        User32.ReleaseDC(hWnd, hDC); //Free hDC
    }
    return bitmap;
}

The problem is, that it can only work if hWnd handles the WM_PRINT and WM_PRINTCLIENT messages correctly. For example, one can test it sucessfully using

UIntPtr hWndMyButton = User32.CreateWindowEx(
    WS_EX.NONE, "Button", " HELLO WORLD! :) ", WS.CHILD,
    5000, 5000,
    200, 30, User32.GetDesktopWindow(), (UIntPtr)0x8807, UIntPtr.Zero, UIntPtr.Zero);
Bitmap capture = CaptureHWnd_WM_PRINT(hWndMyButton);

, but for the most cases it only results in a black image of the size of hWnd. Note that hWndMyButton is far outside the screen bounds (at position {X=5000,Y=5000}), but can still get captured. That is the reason I need WM_PRINT.

The questions is now, how can I force hWnd to handle the print messages correctly on at least Windows 7?

So far, I found this solution for Windows 2000 and would like to get a similar solution for Windows 7, or preferred a generic solution for all Windows systems that support WM_PRINT. Unfortunately I could not find anything for other systems than Windows 2000.

0条回答
登录 后发表回答