10

I am building a WinForms application that records steps of an external process by taking its screenshots every 500 milliseconds. I am using following code:

Bitmap bmp = new Bitmap(width, height,PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.left,
                 rect.top,
                 0,
                 0,
                 new Size(width, height),
                 CopyPixelOperation.SourceCopy);

The code is working fine but the only problem is that when I open a dialog box from the external process's window (For ex: Opening Save As... dialog box in Notepad), the screenshot freezes to the original window instead of showing dialog box.

My theory is that because of following code that I am using to detect if the application lost focus then just revert to last saved screenshot:

if (GetForegroundWindow() != proc.MainWindowHandle) //proc is just a process from system process list by Process.GetProcessesByName()
{
   return LastScreenShot;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();

But this code is necessary for showing user only the application that is being recorded not any other application that is being dragged inside the recording area or the part of desktop inside the recording area. Also, when I click the menu, it sometimes shows the menu freezed in faded position, sometime not showing at all or showing but navigational highlighting not visible in the screenshot.

So is there any way I can solve this problem?

Similar question is asked here Screen Capture Not Capturing Dialog Boxes in My application but it doesn't solve my problem because answer is using the same code and my application does not take the screenshot of the whole desktop.

Community
  • 1
  • 1
Aishwarya Shiva
  • 3,460
  • 15
  • 58
  • 107
  • It might be worth checking out: http://stackoverflow.com/questions/1363167/how-can-i-get-the-child-windows-of-a-window-given-its-hwnd . So maybe you can validate against all child windows instead of only to proc.MainWindowHandle – Johan Mar 03 '17 at 16:43
  • @Johan Thanks for the link but I already read that thread. I am getting the handles for the child windows but the question is how to include them in the screenshot with the main window? And I am also validating but still dialog boxes such as OpenFileDialog and SaveFileDialog are still not coming in screenshot. – Aishwarya Shiva Mar 03 '17 at 16:47
  • Ah okay. I thought of it as you say you think it might be because you revert to the last saved screenshot when the main handle is not the foreground window. – Johan Mar 03 '17 at 16:57
  • @Johan the problem is that the main menu and dialog boxes are becoming foreground window when I open them. – Aishwarya Shiva Mar 04 '17 at 13:55
  • As you say, maybe it's just your test `GetForegroundWindow() != proc.MainWindowHandle` that is not valid. why don't you just check the foreground or modal window belongs to the process you're recording? (you can use the `GetModalWindow` from my answer here: http://stackoverflow.com/questions/19147/what-is-the-correct-way-to-create-a-single-instance-application/3808173 ) – Simon Mourier Mar 04 '17 at 16:55
  • I have tested the code at the link that you gave. It works fully, dialogboxes has been captured too. – Berkay Yaylacı Mar 07 '17 at 13:10
  • @SimonMourier From your linked question I was able to form this code: `IntPtr ChildWindow = FormUtilities.GetModalWindow(proc.MainWindowHandle); if (ChildWindow != GetForegroundWindow()) { if (CurrentWindow != hwnd) return LastScreenShot; }` It's not working but can you please change it according to that code and post as answer? – Aishwarya Shiva Mar 07 '17 at 19:30
  • I am using winapi for this [Getting screenshot of a child window running OpenGL in it (Windows)](http://stackoverflow.com/a/18107834/2521214) And it has no problem with dialogs,GL or DX windows ... If you are not screenshoting desktop but specific window instead then you will not have the other windows included in the image.... – Spektre Mar 10 '17 at 12:03

2 Answers2

1

Unless you can get hold of that Dialog boxes window handle and determine if its a child of the handle you care about (unlikely) you'd basically have to roll with it.

You could build in a latency to allow the continuing of screenshot taking for a set period of time when the dialog situation occurs in the hope that control will be returned to the original handle after a given time period, and only if it doesn't return after that time period stop your capture process.

Or

You can get hold of the process information for a window handle using something like this ....

Find process id by window's handle

... if the owning process is the same one the application likely still has focus (unless something very odd is going on).

Community
  • 1
  • 1
War
  • 8,539
  • 4
  • 46
  • 98
  • I tried this `int activeProcId, threadProcId ; GetWindowThreadProcessId(proc.MainWindowHandle, out activeProcId); GetWindowThreadProcessId(GetForegroundWindow(), out threadProcId); if (activeProcId == threadProcId) MessageBox.Show("Yes, the threads are from same process.");` It's working for main window but not dialogs. – Aishwarya Shiva Mar 07 '17 at 19:22
  • according to this: http://stackoverflow.com/questions/12597599/c-sharp-get-parent-process-from-window-handle ... it implies that you can use that with a dialog. Some setting or arg missing perhaps? – War Mar 08 '17 at 09:23
  • Try something like this ... http://stackoverflow.com/questions/2281429/how-to-enumerate-all-windows-within-a-process – War Mar 08 '17 at 09:24
0

But this code is necessary for showing user only the application that is being recorded not any other application that is being dragged inside the recording area or the part of desktop inside the recording area.


I am getting the handles for the child windows but the question is how to include them in the screenshot with the main window?

I think in this case your only option would be to capture both screens (the main window and the dialog) and combine them yourself using BitBlt() or something similar.

Also keep in mind that the dialog could be moved outside the main window area or even to a second screen. So even if you could capture both in a single shot, that's probably not what you want. By combining them yourself, you can place the dialog back on top of the main screen.

Of course you would have to do the capture on a window handle (using PrintWindow()) and not a screen or desktop region. See here and here for some examples on how to do that.


if (GetForegroundWindow() != proc.MainWindowHandle)

This is probably the reason the dialog isn't captured (i.e. no capture is done at all), but even if you fix or remove that, another window could be dragged inside the region even when a dialog is open.

Community
  • 1
  • 1
Danny_ds
  • 11,201
  • 1
  • 24
  • 46