What is the best way to capture an application window in C#? - c#

I've developed a simple windows forms application to capture the windows of a video chat application (inbound, aka Remote, and outbound, aka Local).
I use unmanaged Windows API code for this. Here is the Capture code:
// Set Local Window
localHandle = FindWindow(null, "local");
// Backup parent window for local
prevLocalHandle = GetParent(localHandle);
SetParent(localHandle, this.pBoxLocal.Handle);
SetWindowLong(localHandle, GWL_STYLE, WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER | WS_DISABLED));
MoveWindow(localHandle, 0, -TOP_BAR_HEIGHT, this.pBoxLocal.Width, this.pBoxLocal.Height + LOWER_BAR_HEIGHT, true);
// Set Remote Window
remoteHandle = FindWindow(null, "remote");
// Backup parent window for remote
prevRemoteHandle = GetParent(remoteHandle);
SetParent(remoteHandle, this.pBoxRemote.Handle);
SetWindowLong(remoteHandle, GWL_STYLE, WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER | WS_DISABLED));
MoveWindow(remoteHandle, 0, -TOP_BAR_HEIGHT, this.pBoxRemote.Width, this.pBoxRemote.Height + LOWER_BAR_HEIGHT, true);
Here is the Return code:
// Return Windows
SetParent(localHandle, prevLocalHandle);
SetWindowLong(localHandle, GWL_STYLE, (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX));
MoveWindow(localHandle, 0, 0, NORMAL_WIDTH, NORMAL_HEIGHT, true);
SetParent(remoteHandle, prevRemoteHandle);
SetWindowLong(remoteHandle, GWL_STYLE, (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX));
MoveWindow(remoteHandle, 0, 0, NORMAL_WIDTH, NORMAL_HEIGHT, true);
The goal is to go from this:
To this:
And then back again! :)
There are currently two issues with my way of doing things:
first of all, when I return the windows to the video chat application, ocasionally a black rectangle is left on my top-left corner of the screen. It disappears when I refresh the area.
second and most important of all, there are times that when I capture the window of the application, I also capture its toolbars (although the measurements I supply are just the ones regarding the video area of the window).
Is there a better way of doing this? Even if its just better functions! Remember: I want to obtain the windows of the video chat application and return them afterwards.
Thanks in advance for any tips!

Ok well the first issue you stated is an easy one to fix. You can just call Refresh() when you return the windows. However if you meant there is a black rectangle on your main desktop not a window then you can use http://msdn.microsoft.com/en-us/library/bb776346(VS.85).aspx which will allow you to force the whole desktop to refresh.
As for the second issue you are having since you are already messing with the Window Long method why not just remove all borders, I believe what might be happening is the border may have a "ThickFrame" which you specify in your return methods but not capture methods so that may be why you are getting toolbars and borders. You can check this by calling GetWindowLong storing that value and seeing what is there, this way you can know exactly what to remove.
Although I am not sure as to what the use of this application is. I believe what you are doing may be the only way to do it, since you are manipulating external screens.

Related

How to keep a window on top of a specific window only?

My application is a Winforms transparent overlay attached to another application.
Since there can be multiple overlays running at one time for different apps, I need my overlay to be always on top, but only over its target application.
What I tried was to bring the form to the foreground with
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);
When target app gains focus through my WindowHook eventType == NativeMethods.SWEH_Events.EVENT_OBJECT_FOCUS.
The problem is that the overlay gains focus after going on top, not allowing me to interact with the app underneath. Like if I try to drag the target app it will block me at first, and if I try to click anywhere nothing happens despite being clickthrough (works when I set Winforms TopMost = true;).
I would consider setting the form topmost when target app gains focus, but there is no event to check for loss of focus to undo the topmost, and even then it would be less than an elegant solution, to say the least.
There must be a simpler way to keep my overlay on top of its target app process. Or the very least, a way to bring the form on top without gaining focus / disrupting my mouse events.
So EVENT_SYSTEM_CAPTUREEND = 0x0009 gets called after any interaction with the target process window. This means I can set my form topmost
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
and undo it with
SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); on EVENT_SYSTEM_CAPTUREEND.
I'm not a fan of this solution but works and it's seamless.

How to focus a process window over a running webbrowser window in c#?

I have a small Windows Forms program (agent) running in the Systray. The purpose of this agent written in C# is to start other programs, focus them and putting them into the foreground.
This agent receives the commands to start other programs from the browser and reports the successful start back to it. This means the browser is always up and running.
Starting external programs from the agent is working fine, but I have much trouble to get the focus and foreground topic to work. It seems like the browser is always in focus and the programs started from the agent are moved right behind the browser window.
I have tried the following methods from the User32.dll without success:
SetForegroundWindow(IntPtr handle)
SwitchToThisWindow(IntPtr hWnd, bool fAltTab)
The strange thing is that if I am running the agent in my Visual Studio the focus works perfect!
Help is much appreciated, since I am new to windows internals.
Update 1: I noticed if for instance a powershell window (could be anything arbitrary) lays over the browser window, focusing the started process window works as expected.
Update 2: I was able to get the right focus by calling
SetWindowPos(browserProcess.MainWindowHandle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
on the browser process window and
SetWindowPos(process.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
on the started process window. But I think this is a bad approach since the browser window may vanish behind other open windows. I have also tried
SetWindowPos(browserProcess.MainWindowHandle, process.MainWindowHandle, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
to switch the Z order of the browser window and the process window, but without success.
Question: How I can switch the Z order of the browser process and the started process?
Seems like SendKeys.SendWait("%"); was the magic I needed.% is the ALT key.
From the documentation of LockSetForegroundWindow
The system automatically enables calls to SetForegroundWindow if the user presses the ALT key or takes some action that causes the system itself to change the foreground window (for example, clicking a background window).
Calling AllowSetForegroundWindow returned FALSE in the first place so I was not able to set the foreground window. A call to GetLastError revealed the error code ERROR_ACCESS_DENIED. Calling SendKeys.SendWait("%") before User32dll.SetForegroundWindow(windowHandle) enables calls to SetForegroundWindow to be successful.
To put it together it looks like this:
var windowHandle = User32dll.FindWindow(null, nameOfWindow);
SendKeys.SendWait("%");
User32dll.SetForegroundWindow(windowHandle);
If the window is minimized, using User32dll.ShowWindow(windowHandle, User32dll.SW_RESTORE) is sufficient.
How do you retrieve the handle for that window?
This works for me:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
SetForegroundWindow((IntPtr)handle);

How to monitor a window's position with Win32?

I need to draw an overlay using WPF on top of a number of Win32 windows. In order to draw the overlay in the correct place, I will need to hook into the window move, but have no idea how to do this. Which Win32 calls should I be looking at?
SetWinEventHook
var hook = SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART,
EVENT_SYSTEM_MOVESIZEEND, NULL, WinEventProc,
0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);

Removing WS_BORDER and WS_CAPTION from windows styles doesn't work

I created a small app in C# that removes border and caption from window, then it sets size to the user's resolution and centers it. It's a utility for me to use when I want to play games in windowed mode without being annoyed by the borders. Everything works fine with most games, but I tried to use it on a recently released game Alpha Protocol and it doesn't work. I could almost say that the game reverts my changes, but I'm not sure how to tell if that's true or not. I'm using imported API functions MoveWindow, SetWindowLong and SetWindowPos.
Snippet:
Win32.MoveWindow(hWnd, 0, 0, Convert.ToInt32(sizeXText.Text), Convert.ToInt32(sizeYText.Text), true);
Win32.SetWindowLong(hWnd, GWL_STYLE, Win32.GetWindowLong(hWnd, GWL_STYLE) & ~WS_CAPTION & ~WS_BORDER);
Win32.SetWindowPos(hWnd, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_DRAWFRAME);
Every time SetWindowPos is called it sends a message: WM_WINDOWPOSCHANGING to the window with a writable copy of the SetWindowPos parameters. This gives the window the chance to validate and change the parameters. Many window's automatically react to repositioning and sizing to "smartly" manage their non-client decorations - which I guess is what is happening here. The OnWindowPosChanging code is re-setting the non client decorations just before the DRAWFRAME is handled.
You should use Spy++ to investigate the game's window structure.

Programmatically handling the Vista Sidebar

Is there an api to bring the vista side bar to the front (Win+Space) programatically and to do the reverse (send it to the back ground).
Probably using SetWindowPos you can change it to be placed the top / bottom of the z-order or even as the top-most window. You would need to find the handle to the sidebar using FindWindow or an application like WinSpy.
But after that something like.
Sets the window on top, but not top most.
SetWindowPos(sidebarHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NORESIZE);
Sets the window at the bottom.
SetWindowPos(sidebarHandle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NORESIZE);
This is my best guess on achieving what you asked, hopefully it helps.
You probably shouldn't do it at all, since such action may annoy the user when executed at the wrong time (95% of cases*), just like stealing focus with a "Yes/No" prompt.
Unless your product's task is to toggle the sidebar of course. ;)
There's no official API for that anyway.
*Purely hypothetical figure

Categories