views:

67

answers:

2

I am moving a window (belonging to another process) to the front in order to take a screenshot of it. I am able to do this using SetForegroundWindow, however that function returns immediately. The other process takes a varying amount of time to redraw its main window (which requires it to access a database) so I cannot be sure that when I take the screenshot the window is fully rendered. Sometimes all I get in the screenshot is the outline of the target window, on top of whatever window was previously in the foreground.

Is there a reliable way to wait until another process's window is fully painted? I suspect there isn't but it's worth a shot. Maybe there's a message I can send to the window that will have this effect?

Note: The implementation language is not important but I need a solution using the native Windows API, either directly from C/C++ code or via P/Invoke (e.g. from C# or VB.NET). Unfortunately I cannot use any WinForms functions.

+1  A: 

UpdateWindow will repaint the window if any part of it needs repainting, and then return.

Chris Becke
I still have the same problem. `UpdateWindow` returns too soon, before painting is complete.
finnw
@finnw: I think after the `UpdateWindow`, I'd call `WaitForInputIdle`, and see if that does the trick. I haven't used `WaitForInputIdle` in this situation before, so I can't guarantee it'll be effective, but I'd guess there's at least a pretty decent chance...
Jerry Coffin
@Jerry Coffin IIRC WaitForInputIdle only works when the process is starting up.
Anders
That made no difference.
finnw
This should definitely work. Ensure you've got the right handle and get a TRUE return. If the window is querying the dbase in a background thread and delay-renders the results then there's little you can do but sleep.
Hans Passant
@Anders: According to MSDN: "WaitForInputIdle can be used at any time, not just during application startup." (http://msdn.microsoft.com/en-us/library/ms687022(VS.85).aspx). Unfortunately, it also says: "However, WaitForInputIdle waits only once for a process to become idle; subsequent WaitForInputIdle calls return immediately, whether the process is idle or busy."
Jerry Coffin
@Hans. `UpdateWindow` returns 1 and `WaitForInputIdle` returns 0
finnw
Those are "it worked" return codes. Is this an MDI app?
Hans Passant
I cannot give much information about the original app, but it is not MDI. I don't think it is specific to that app because I've seen the same problem occasionally with simpler apps such as Windows Task Manager. Another good example is OpenOffice. It can be slow to repaint but AFAIK painting never depends on network access.
finnw
A: 

I am getting good results using the following strategy:

  1. Call PrintWindow to render the target window into a bitmap and discard the result.
  2. Call SetForegroundWindow to bring the target window to the front (this also triggers a repaint, but the repaint is usually fast because the synchronous WM_PRINTCLIENT has already forced the process to page in the data required for rendering.)
  3. Sleep for a short time (10ms)
  4. Take a screenshot using BitBlt
  5. Restore the original Z-order of the target window
finnw