Link to home
Start Free TrialLog in
Avatar of DavidDunn
DavidDunn

asked on

Is ther a problem with the WM_PRINT message

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.
Avatar of ete
ete

Could you give a small code fragment to clarify the situation?
You must specify PRF_CHILDREN in LPARAM when sending WM_PRINT message to the parent window.

Avatar of DavidDunn

ASKER

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.
I would like to send you a bitmap file showing you the result, but I don't seem to be able!

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

ASKER CERTIFIED SOLUTION
Avatar of galkin
galkin

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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).
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.
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.
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.