How do I get the active ChildWindow of an application? - c#

I have this problem: I have an handler to the mainWindow of a certain application, and I want to simulate a keypress on that application...
I'm using sendMessage/postMessage api calls to do this. The reason why I don't use the .Net SendKeys function or the keybd_event of the win32 api, is that they simulate the keypress at a global level. In my case, the target application is not the top-active one (other application may be running in a higher z-level, hence covering the target app).
The problem with sendMessage and postMessage is that you must pass the handler of the exact childwindow where you want the key to be pressed. For example, in notepad, if I send the key to the handler of the mainWindow, nothing happens, I have to send the key to the handler of the child window that basically consists of the white canvas where you can write.
Obtaining the handler to the active child window is the problem. In the beginning, I was using the GetTopWindow or GetWindow(GW_CHILD) api calls, as it returns the most active child window. What I was doing was to keep calling the GetWindow(GW_CHILD) until I got a childwindow that had no more childWindows. This works ok for some applications like notepad or paint. However, in some cases (like firefox for example), it doesn't work. The reason for that is that the parent window has the whole firefox area, and its childwindow has the opened WebPage (like google). So, when I ask for the most active child window of the mainWindow, it returns the only child window it has, which is the one corresponding to the Webpage area. It only works if the active window is that one (like if the user is writing something on a textbox of a certain page). But if what is active is, let's say, the address bar, it doesn't work because the active window is not the child window but actually the parent... and I can't get this information programatically.
I actually found a way of doing this, using the GetGUIThreadInfo api call, using the following code:
// get thread of the main window handle of the process
var threadId = GetWindowThreadProcessId(firefox.MainWindowHandle, IntPtr.Zero);
// get gui info
var info = new GUITHREADINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
if (!GetGUIThreadInfo(threadId, out info))
throw new Win32Exception();
// send the letter W to the active window
PostMessage(info.hwndActive, WM_KEYDOWN, (IntPtr)Keys.W, IntPtr.Zero);
And it works very well: If the address bar is active, it sends a "W" letter to the address bar. If the search textBox of google is active, it sends the "W" letter to it... Perfect! However, this method can't be used by me for a simple reason:
If the target application is not the active window of the operating system, the ThreadInfo structure comes empty. For example, if I'm targetting firefox, it works if firefox is active (the top-most application, the focused/active one), but if, let's say, notepad is on top of firefox, it doesn't work, it is unable to get the active window handler.
I know I can solve this by using the setForegroundWindow api call to activate the target application and then capture the handler of the active child-window but I didn't want to have to bring the target app to the foreground.
I've also tried other techniques like AttachThreadInput() and GetFocus() api calls, which also works, but has the same problem: If the target application is not the active windows application, it doesn't work.
So basically I need to find some way of getting the handler to the active childwindow of an application even if that application is not the top-active one.
Any ideas?
Thanks

You might want to check out the EnumChildWindows function.

If everything else fails, here is another idea: You might want to consider using a WH_CBT or a WH_CALLWNDPROC hooks to monitor which child window of the target thread has been focused last.
Install a CBT hook (WH_CBT) and listen for the HCBT_SETFOCUS notification.
Or use a WH_CALLWNDPROC hook and listen for the WM_SETFOCUS message.
Don't do much in the hook proc or you'll hog down the system resources. Just save the needed information and post yourself a custom message to process it later.

Related

Determine if a dialog is showing

In my WPF application, I have multiple classes each of which implements a message loop. All of these message loops then run in parallel when I create objects of these classes. These objects may show a dialog box at times. The main application thread must keep an eye on these objects and make sure none of them is stuck with the dialog (and press Cancel (Escape key) if it determines such cases). What is the correct way to determine the active foreground window of a thread? I know there is a GetForegroundWindow() API, but it appears as if it works at system level and not thread level.
There's no such thing as the per-thread active foreground window. So what you are explicitly asking for does not have an answer.
Probably the right way to go here, using Win32 at least, is to enumerate top-level windows with EnumWindows. Then use GetWindowThreadProcessId to identify that the window is associated with one of your threads. Finally use GetClassName to identify that the window is a file dialog. Then feel free to do whatever dastardly thing it is you want to do to the window!
On the other hand, this sounds like a perfect candidate for UIAutomation. You are automating testing of UI. UIAutomation will be able to find these file dialog windows and press buttons on them.

detect currently executing browser and tab/window

I know how to detect already running browsers. Now, I want to know how to detect browser that is currently executing and I also want to know how to detect if new tab/window opens of some browser in c# winforms.
e-g
if user click on x browser; my program should tell x browser is running. now if user opens a new tab in that browser then my program should tell "tab opening in x browser".
I did research but could not find exact!
Thanks
There are two ways to do that, both of them use P/Invoke as there is no managed code for such thing :
Polling :
You program should call GetForegroundWindow periodically then GetWindowText and test if the open focused Window is a browser or not and that's also the active tab title (page title).
Event listening :
You should SetWinEventHook on a system-level event : EVENT_SYSTEM_FOREGROUND to detect the new focused window. You can then SetWinEventHook on an object-level EVENT_OBJECT_NAMECHANGE or EVENT_OBJECT_CREATE for every browser's window to detect tab changes.
Tips :
The client thread that calls SetWinEventHook must have a message loop in order to receive events.
For out-of-context events, the event is delivered on the same thread that called SetWinEventHook.
When you use SetWinEventHook to set a callback in managed code, [you should explicitly tell] the garbage collector not to move the callback.
While a hook function processes an event, additional events may be triggered, which may cause the hook function to reenter before the processing for the original event is finished. The problem with reentrancy in hook functions is that events are completed out of sequence unless the hook function handles this situation. For more information, see Guarding Against Reentrancy.

SendInput to minimized window

Is it possible to utilize the sendInput function on windows that currently do not have focus, and maybe through the use of multithreading, sendinput to multiple minimized windows at the same time, or send input to one window while you're working on another window?
I'd like to do something like this in c#
thanks in advance.
You can only use SendInput to send input to the HWND with keyboard focus. Furthermore the window must be attached to the calling thread's message queue, so one cannot simply SetFocus either.
You'll need to get the window's thread id with GetProcessIdOfThread.
When you have the thread id you can use the AttachThreadInput function to attach your thread to the other threads input processing.
After all this you can probably use SetFocus and SendInput.
You'll probably want to detach your thread when you've sent your input.
To get access to these method you'll have to use P/Invoke for C# or C++/CLI. PInvoke.net is very handy as a reference. It will be a small chore importing all those functions, but when you are done you should be able to send input to whatever "window" you want.
Also as a side note, I'm not sure if you are aware of this, but in pure Win32 everything is regarded as a window, even a button. If you are unlucky you may have to send the input to the handle of the text control belonging to the notepad application.
That is not possible with SendInput. What you probably want to do is find the messages that were sent to the window by the OS when that particular event was performed then emulate them. You can use Spy++ to attach to the target window and perform your event. Then use SendMessage() and PostMessage() to reproduce the messages that were generated by your event. This will work fine for notepad.
If you do use this method, note that you need to send messages to notepad's child window which you can find with FindWindowEx() with a classname of "edit". For example to type text you could try WM_KEYDOWN. You should note that this method is not necessarily reliable:
http://blogs.msdn.com/b/oldnewthing/archive/2005/05/30/423202.aspx

Block a Processes Child Window?

I am launching a process in C# .NET that for some reason opens a message box when it is initially launched. What I need to do from my application is find some way to either prevent this message box from ever being opened, or basically "click" the OK button on the message box from my code.
The message box is causing the process to hang when we run it as a service, so I now need to find some way to either prevent that box from ever opening or just close it (select OK) from my code.
I have been looking at some Win32 API samples, but I have never had to use it before and it looks a bit strange. Any suggestions would be great!
Edit
Here is a batch file that was supplied as a work around for the issue. However I would much prefer to have it running an executable if possible. The explanation was that using the -supw parameter (to set a password on the server) causes this message box to popup. This batch file is tested and works, but it requires you have a duplicated executable (murmur2.exe) created which is ghetto.
set /p VAR= < superadmin.txt
start murmur2.exe -supw %var%
ping 0.0.0.0 -n 3 > NUL
tskill murmur2
murmur.exe
PostMessage(FindWindow("#32770",*Title of the message box*),WM_CLOSE,NULL,NULL);
Since there might(with a very slim chance) be a same window opened with the same class name with the same title at the same time, this is not a fail proof way. However the alternatives are simply an overkill. If you really want to make it fail proof, you'll have to call EnumWindows to enumerate all top-level windows, then for each window, call GetWindowThreadProcessId and compare the window's process id with your launched process' id. If they are equal, you can then call GetClassName and GetWindowText to compare the class names(always "#32770" for common dialogs) and the title of the message box to verify that you're trying to close the right window. Once it's done, you can call PostMessage with WM_CLOSE using the hWnd that you found.

Prevent Process 'A' from spawning Process 'B' which then shows up on top of what should be the "TopMost" Process 'C'

I have a windows form application which needs to be the TopMost. I've set my form to be the TopMost and my application works as I'd like it to except for in one case.
There is a 3rd party application (referred to as player.exe) that displays SWF movie files on a portion of the screen that popup on top of my application.
Using Process Monitor I determined that player.exe application calls
flash.exe <PositionX> <PositionY> <Width> <Height> <MovieFile>
in my case:
flash.exe 901 96 379 261 somemovie.swf
Since flash.exe is being spawned in a new process after my form has been set to the TopMost it is appearing on top of my application.
First thing I did was make my application minimize the player.exe main application window hoping that this would prevent the Flash from appearing also. But, unfortunately it doesn't... even with the window minimized whenever the flash movie starts it shows up at the pixel location (901,96). I then tried creating a timer to keep setting the form.TopMost property to true every 10ms. This sort of works but you still see a very quick blip of the swf file.
Is there some type of Windows API call which can be used to temporarily prevent player.exe from spawning child processes which are visible? I admit it sounds a little far fetched. But, curious if anyone else has had a similar problem.
Addendum:
This addendum is to provide a reply to some of the suggestions layed out in Mathew's post below.
For the emergency situation described in the comments, I would look at possible solutions along these lines:
1) How does the third party application normally get started and
stopped? Am I permitted to close it
the same way? If it is a service, the
Service Control Manager can stop it.
If it is a regular application,
sending an escape keystroke (with
SendInput() perhaps) or WM_CLOSE
message to its main window may work.
Easiest way to close the app is to CTRL-ALT-DEL, then kill process. -OR-
The proper way is to Hold ESC while clicking the left mouse button... then input your username and password, navigate some menu's to stop the player.
There is no PAUSE command... believe it or not.
I don't think using WM_CLOSE will help since minimizing the application doesn't. Would that kill the process also? If not, how do you reopen it.
2) If I can't close it nicely, am I permitted to kill it? If so,
TerminateProcess() should work.
I can't kill the process for two reasons. 1) Upon relaunch you need to supply username/password credentials... There may be a way to get around this since it doesn't prompt when the machine is rebooted but... 2) Whenever I kill the process in task manager it doesn't die gracefully and asks if you want to send an error report.
3) If I absolutely have to leave the other process running, I would try
to see if I can programmatically
invoke fast user switching to take me
to a different session (in which there
will be no competing topmost windows).
I don't know where in the API to start
with this one. (Peter Ruderman
suggests SwitchDesktop() for this
purpose in his answer.)
I got really excited by this idea... I found this article on CodeProject which provides a lot of the API Wrapper methods. I stopped implementing it because I think that in order for desktop's to work you must have explorer.exe running (which I do not).
EDIT2: On second thought... maybe explorer.exe isn't needed. I'll give it a try and report back.
Edit3: Was unable to get the code in that article working. Will have to put this on hold for a moment.
Answer Summary
As one might have expected, there is no simple answer to this problem. The best solution would be to problematically switch to a different desktop when you need to guarantee nothing will appear over it. I was unable to find a simple C# implementation of desktop switching that worked and I had a looming doubt that I would just be opening a whole new set of worms once it was implemented. Therefore, I decided not to implement the desktop switching. I did find a C++ Implementation that works well. Please post working C# virtual desktop implementations for others.
Setting the TopMost property (or adding the WS_EX_TOPMOST style to a window) does not make it unique in the system. Any number of topmost windows may be created by any number of applications; the only guarantee is that all topmost windows will be drawn 'above' all non-topmost windows. If there are two or more topmost windows, the Z-order still applies. From your description, I suspect that flash.exe is also creating a topmost window.
Aside from periodically forcing your window to the top of the Z-order, I think there is little you can do. Be warned, however, that this approach is dangerous: if two or more windows are simultaneously trying to force themselves to the top of the Z-order, the result will be a flickering mess that the user will likely have to use the task manager to escape.
I recommend that your program not attempt to meddle with other processes on the computer (unless that is its explicit purpose, e.g. a task manager clone). The computer belongs to the user, and he may not value your program more highly than all others.
Addendum:
For the emergency situation described in the comments, I would look at possible solutions along these lines:
How does the third party application normally get started and stopped? Am I permitted to close it the same way? If it is a service, the Service Control Manager can stop it. If it is a regular application, sending an escape keystroke (with SendInput() perhaps) or WM_CLOSE message to its main window may work.
If I can't close it nicely, am I permitted to kill it? If so, TerminateProcess() should work.
If I absolutely have to leave the other process running, I would try to see if I can programmatically invoke fast user switching to take me to a different session (in which there will be no competing topmost windows). I don't know where in the API to start with this one. (Peter Ruderman suggests SwitchDesktop() for this purpose in his answer.)
You can use the Process class to start flash.exe directly - and use an appropriate ProcessStartInfo settings to show the window in a hidden state - or with a WindowStyle of hidden or minimized.
You could also consider using the SetWindowsHookEx API to intercept the process start API calls, and when the process is flash.exe run some code to restore you window to top-most status.
Matthew's answer is excellent, but I suspect you may be asking the wrong question. Why does your application need to be topmost? If you're trying to create a kiosk or some such, then topmost is not the way to go.
Edit: After reading your response to Matthew's comment, I'd suggest creating a new desktop and switching to it before displaying your alert. (See CreateDesktop and SwitchDesktop in MSDN.)

Categories

Resources