Forcing a hidden window to paint to an arbitrary DC

Started by
6 comments, last by ApochPiQ 18 years, 10 months ago
In short, I have a hidden window with an ActiveX control on it of some form or another, and I need to get the pixel data from that control so that I can copy it to a Direct3D texture surface. The problem is that I cannot seem to force the window to paint; it simply does nothing, since it is not visible. I have gotten this to work with one control, the Macromedia Flash control. To do so, I simply called SendMessage(hWndFlash, WM_PAINT, (WPARAM)hDC, 0); where hDC was a device context that I made myself, and that had a bitmap selected into it that I also had created myself. I could then call GetDIBits() on that bitmap to copy the pixel data to a locked Direct3D surface. Works fine. However, WM_PAINT's documentation states that while the wParam can refer to an arbitrary DC on which the control should paint, this is only for certain controls that choose to support this, and other windows can safely ignore the wParam for the WM_PAINT message. This is the case when working with, for example, the WebBrowser control in "system32\shdocvw.oca". No matter what I do, it paints to it's own DC. And if I merely try to copy data from its DC (using GetDC() or GetWindowDC() in order to get the handle to the DC), all I really get is data from the desktop. If the WebBrowser control's container window is visible and on top, I do indeed have access to the control's pixel data, as the control is visible from the desktop's DC. But considering my program is (in release mode) a fullscreen Direct3D application, this is entirely impossible. I somehow need to trick the window into painting to a DC/bitmap that I create myself, and force it to paint regardless of if it is invisible, behind other windows, or off the edge of the screen. I've looked in the documentation and haven't found anything that looks promising aside from WM_PAINT's wParam, which doesn't work. And I've tried all sorts of things even if they don't sound too promising. I've tried invalidating the entire window, forcing the clipping region to include the whole window, and all sorts of stuff. I've trying SendMessage(), PostMessage(), UpdateWindow(), RedrawWindow(), and InvalidateRect(), in all sorts of combinations even if they didn't make sense. I've tried getting DCs and selecting my own bitmaps into them before telling the window to paint. Nothing gives me any success. So if someone has had to force a window to paint before and knows what needs to be done (if it is in fact possible), I'd love to hear it.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Advertisement
Well, according to MSDN, WM_PAINT should not be sent by applications. The recommended method is to send WM_PRINT (or WM_PRINTCLIENT for the client area) to the control. First use CreateCompatibleDC to get a device context compatible with the control's DC, and then send WM_PRINT. You may also try calling InvalidateRect on the control and using BitBlt to copy the image onto your own DC, but this may not work since the target window is invisible.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

While I totally was unaware of WM_PRINT/WM_PRINTCLIENT, and while it looked like that would solve my problem, unfortunately it did not. It appears that the Web Browser control doesn't implement WM_PRINTCLIENT; the message simply gets ignored. But this page gave me some hope that I could trick the window into receiving a WM_PAINT message, and making the BeginPaint() function return my DC, rather than the window's own DC. No luck. As far as I can tell, the Web Browser control never calls BeginPaint() or EndPaint().

If I use the window handle for the ActiveX container, rather than the ActiveX control itself, I can get all the nonclient portions of the window drawing just fine using the WM_PRINT message and all but the PRF_CHECKVISIBLE flag; the client area is merely the background color, though. So I know that the rest of my code is working. It's just that the WM_PRINTCLIENT message gets ignored. I suppose I'm more or less out of options then, other than finding another way to render webpages. Which might be worthy of another thread, I suppose, if some searching doesn't quickly bring up any solutions.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
How are you creating the WebBrowser control on the window? Specifically, what style bits are set on the control? Something seems out of place... subclassing the window and changing the hooked device context seems like it should work regardless of how the control internally responds to WM_PAINT.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I've done exactly what you are looking to do but it was a crapload of work. I actually went a bit further and got interaction working. Check out http://www.logiccubed.com , theres screenshots and a demo. I have been working on my engine to adapt it for use in games and other apps but its still going to take some time. I wont tell you haw I did it, but if you can wait for me to finish you can use my lib, or if you can wait for longhorn and DX10 there will be "shared surfaces" which SHOULD allow you to do these types of things more easily.
Quote:Original post by ApochPiQ
How are you creating the WebBrowser control on the window? Specifically, what style bits are set on the control? Something seems out of place...
I use a CAxWindow class from the ATL to host the ActiveX control. (I'll admit that I didn't take the time to understand the ATL stuff. I got it to work and that was good enough.) I was initially using a style of WM_POPUP, and an extended style of 0. I later changed it to WM_OVERLAPPEDWINDOW just to see if I could get the nonclient stuff, which I could.

I think I've tried it briefly, but I could play around a bit more with SetWindowLong on the contol's window itself, rather than merely the container's window, but I don't know what different styles would possibly accomplish anything.
Quote:subclassing the window and changing the hooked device context seems like it should work regardless of how the control internally responds to WM_PAINT.
But if the control just uses it's own DC without even bothering to call BeginPaint() to get the DC, then this other DC that was specified by WM_PRINTCLIENT goes completely unused. Because the swapping of the typical DC for my special DC occurs in the hooked BeginPaint() function. Without a BeginPaint() call, I don't know how to trick the control into using another DC.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Just so the curious know, I managed to get it to at least kinda work, though I'm not quite sure what I changed to make it do so. I'll be experimenting more with it tomorrow. If I find anything out, I'll post it. But at the moment, it seems to have been largely an issue of order. I was creating my DC/bitmap immediately after creating the control. Now I create my DC/bitmap immediately after calling Navigate on the Web Browser control. I do remember somewhere mentioning that the control is invisible by default, but the Navigate function automatically makes it visible, so maybe I was having difficulty getting it's DC (in order to create a compatible one) before it was visible. I dunno. Like I said, more experimentation.

And thanks for all the help. Just the simple mention of WM_PRINTCLIENT was a huge aid.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Ah, that makes sense... I've never used an embedded WebBrowser control via ATL (just through MFC) so I'm not sure how the semantics of initialization work, but it sounds logical that the device context wouldn't be fully created until the first render was actually needed.

I'll be curious to see what else you turn up on it...

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement