We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now

x

Is ther a problem with the WM_PRINT message

DavidDunn
DavidDunn asked
on
Medium Priority
962 Views
Last Modified: 2013-12-03
When sending a WM_PRINT message to a window specifying a compatible memory device context,  and using a compatible bitmap, then when using 'bitblt' to copy it on to a picture-box the window has not been correctly drawn - any child windows have been offset by and the backgrounds have not been redrawn, even when specifying the correct flags.
Comment
Watch Question

ete

Commented:
Could you give a small code fragment to clarify the situation?

Commented:
You must specify PRF_CHILDREN in LPARAM when sending WM_PRINT message to the parent window.

Author

Commented:
I've tried using the various flags. The problem can best be seen when trying to WM_PRINT an MDI form and a Child form together. The child form is offset by a small amount - leaving an unerased border around the MDI client area.

Author

Commented:
I would like to send you a bitmap file showing you the result, but I don't seem to be able!

Author

Commented:
The problem occurs only when there is a client and a non-client area to be printed.
The background of the child windows are not being erased even when
PRF_ERASEBKGND is specified. The child windows are offset by a small amount
which looks like its the border width.

Below is the visual basic code which prints a window (given its handle) on to a
picture-box. This code actually uses StretchBlt rather that BitBlt.

Public Sub PrintWindow(ByVal hWnd As Long)

    Dim hWndDC As Long
    Dim WindowRectangle As RECT
    Dim WindowHeight As Long
    Dim WindowWidth As Long

    ' Calculate the width and height of the window
    GetWindowRect hWnd, WindowRectangle
    WindowWidth = WindowRectangle.Right - WindowRectangle.Left
    WindowHeight = WindowRectangle.Bottom - WindowRectangle.Top

    ' Get a DC for the window and create a compatible memory DC
    hWndDC = GetWindowDC(hWnd)
    hCompatibleDC = CreateCompatibleDC(hWndDC)

    ' Create a compatible bitmap and select it into the compatible DC
    hCompatibleBMP = CreateCompatibleBitmap(hWndDC, WindowWidth, WindowHeight)
    SelectObject hCompatibleDC, hCompatibleBMP

    ' PRF_CHECKVISIBLE = &H1&
    ' PRF_CHILDREN = &H10&
    ' PRF_CLIENT = &H4&
    ' PRF_ERASEBKGND = &H8&
    ' PRF_NONCLIENT = &H2&
    ' PRF_OWNED = &H20&
    ' Send WM_PRINT message to window specifying the memory DC as the device context
    ' and setting all the PRF_??? flags
    SendMessage hWnd, WM_PRINT, hCompatibleDC, 63

    ' The compatible bitmap is now stretched to the size of the picture-box (picBox)
    StretchBlt picBox.hdc, 0, 0, picBox.Width / Screen.TwipsPerPixelX, picBox.Height / Screen.TwipsPerPixelY, hCompatibleDC, 0, 0, WindowWidth, WindowHeight, SRCCOPY

    ' Clean up
    ReleaseDC hWnd, hWndDC
    DeleteObject hCompatibleBMP
    DeleteDC hCompatibleDC
   
    picBox.Refresh

End Function

Commented:
You problem is very interresting. Unfortunally, I don't know VB but I succeeded in forcing window with child windows to draw itself on memory DC.

This is my code:

CDC memDC;
CDC *pDC = GetDC();
memDC.CreateCompatibleDC(pDC);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(pDC, maxSize.cx, maxSize.cy);
CBitmap *pOldBitmap = (CBitmap *)memDC.SelectObject(&bitmap);
SendMessage(WM_PRINT, (WPARAM)memDC.GetSafeHdc(), PRF_ERASEBKGND|PRF_CLIENT|PRF_NONCLIENT|PRF_CHILDREN);
memDC.SelectObject(pOldBitmap);
ReleaseDC(pDC);

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
Your code is similar to mine (although you haven't bitblt it to a picture-box
- to check that it has been draw correctly). Try your code with an MDI window
(sending the WM_PRINT message to this window to erase the background and draw
the child windows), with an MDI child window in the client area (with child
windows on this MDI child) and then check the position and background colour
of the windows, hopefully you will be able to see the problem. Try your code
with a window that have a visible non-client area as well as a visible client
area - I notice you use GetDC rather than GetWindowDC.

I Have just tried sending a WM_PRINT to the MDI Window of Microsoft Excel - the
problems still there. When a Sheet (an MDI Child Window) is tiled (so the upper
left corner of the Child window is in the upper left corner of the MDI Client
window, In the memory DC the MDI Child Window is offset by about 3 pixels ie. 3
pixels to the right and 3 pixels down). Also, the border of the MDI window (in
the memory DC) is about twice the size of the actual MDI window's border. If you
have Excel (or any other product with and MDI user interface) try sending the
WM_PRINT message to the MDI Window (not the MDI Client window).

Commented:
Yes, you are right, all child windows are really shifted by the width of border(by the way it is subject to check). I don't know the reason but what you can do to workaround the problem is first to let frame to draw on memory DC using WM_PRINT message without PRF_CHILDREN. Then to itterate through all child windows using GetWindow(GW_NEXT) offset window origin by SetWindowOrg according to the top left conner of the child window relative to parent frame. Send WM_PRINT message to each of this window to force each of them to draw itself on the same memory DC.
Yes, it considerably more complicated but it must work.

Author

Commented:
I've tried your suggestion using SetWindowOrg etc. but it does not work. Have you managed to do it using C++?, If so can you show me the code.

Commented:
No I havent tried it myself so I cannot garantee it works. The problem may be caused by the fact parent frame has WS_CLIPCHILDREN style so its DC is automatically excludes all child windows. Since memory DC is compatible with this DC it has the same attributes. I had such a problem and I solved it by the following way. Before forcing child window to draw itself onto memory DC I remomed WS_CLIPCHILDREN style in parent window and restored it after all child windows drew themselves onto this DC.
If it doesn't work you can also create separate memory DC for each child window, force this window to draw itself onto this memory DC and then blit from all child memory DC on one parent memory DC. It is slower since you need to create and destroy memory DC for each window but it must work.
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.