Launching Apps in the Foreground

How can I use ShellExecute to launch an application and force the main window to the foreground?


August 17, 2004
URL:http://www.drdobbs.com/windows/launching-apps-in-the-foreground/184405755

There are two parts to this question-how to find the main window of an application launched with ShellExecute, and how to force that window to the foreground. We'll look at both parts and some code that shows how to ensure the activation will work under all 32-bit operating systems from Microsoft.

Using ShellExecute to launch an application is convenient, particularly if you want to launch it via one of the documents that it manages. For example, if you need to open a Microsoft Word document, you either do this by specifying the path (either full or just the filename) to the Microsoft Word executable and the path to the document, or you can just specify the document. If the latter is the approach taken, ShellExecute will look up the application in the registry that is mapped to the extension of the document (in this case a ".doc file) and execute it according to the values in the registry. This is very powerful (let alone convenient) since the user only has to be concerned with the documents and not about the location of the application.

Although the application is launched, ShellExecute doesn't return any information about the main window handle of the process (it also doesn't return any reliable information about the process ID). For this example, we'll assume that using the SDK function FindWindow will suffice to find the window. A more advanced technique would require using CreateProcess to create the application and receive accurate information about the process ID, mapping top-level windows to processes and comparing these processes to the ID of the one that was launched.

The second part of the question—namely, forcing this window to the foreground-is a bit trickier. Anyone who's been developing Windows applications for a number of years has likely used SDK functions that did one thing under Win16 and changed under Win32; or worse, changed under particular operating system's implementation of Win32. The latter is the situation we find with forcing windows owned by other applications to the foreground.

Normally, you might think to just use the SDK function SetForegroundWindow to do this. Here is what the July 2004 edition of MSDN has to say about what SetForegroundWindow does:

"The SetForegroundWindow function puts the thread that created the specified window into the foreground and activates the window. Keyboard input is directed to the window, and various visual cues are changed for the user. The system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads."

Reading through this description carefully, we see that it's the thread that created the window that is put into the foreground. When launching an application with ShellExecute (or CreateProcess for that matter), it's the newly created process that owns the window, not your process. So calling SetForegroundWindow on the window handle received earlier from the call to FindWindow would not work as expected under some operating systems as we can see below:

"Starting with Microsoft Windows 98 and Windows 2000, the system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:

Curious about why this change was made, I did some poking around. It seems the scuttlebutt is that some of the Microsoft developers in the Systems group decided that the Microsoft developers in the Office group had overused the SetForegroundWindow API and put in code to restrict how it could be used. I have no idea whether this theory is correct or just speculation, but it wouldn't surprise me if it is true. If any Microsoft developers reading this have any insight, drop me an e-mail.

However, there is still a way to use SetForegroundWindow in the "old manner-namely to briefly align the input queues of the threads that own the windows. This is one of those niche corners of the Win32 SDK that many developers haven't explored. When I first discussed this technique with some of the senior developers in my group, they were surprised that input queue alignment was required to support some operating systems when using SetForegroundWindow.

Let's look at some code to see how to do this:

	HWND hOtherWnd = FindWindow( ... );
	if (hOtherWnd)
	{
		DWORD hMyThread = GetWindowThreadProcessId( m_hWnd,NULL );
		DWORD hOtherThread = GetWindowThreadProcessId( hOtherWnd,NULL);
		AttachThreadInput( hMyThread,hOtherThread,TRUE );
		SetForegroundWindow(hOtherWnd);
		AttachThreadInput( hMyThread,hOtherThread,FALSE);
		if IsIconic(hOtherWnd)
			ShowWindow( hOtherWnd,SW_RESTORE );
		elses
			ShowWindow( hOtherWnd,SW_SHOW );
	}

Looking through this code, the interesting part is the use of AttachThreadInput to join two threads together temporarily in order for the messages generated by SetForegroundWindow to be processed properly by the target window. The first call does the join (by passing in TRUE as the third parameter) and the second call reverses the join. This is one of those seldom used, but very useful functions in the SDK that makes it a snap to do something difficult or impossible to do otherwise. Here is what MSDN has to say about this function:

"Windows created in different threads typically process input independently of each other. That is, they have their own input states (focus, active, capture windows, key state, queue status, and so on), and they are not synchronized with the input processing of other threads. By using the AttachThreadInput function, a thread can attach its input processing to another thread. This also allows threads to share their input states, so they can call the SetFocus function to set the keyboard focus to a window of a different thread. This also allows threads to get key-state information. These capabilities are not generally possible."

So, next time you need to send messages from one application to another and want to ensure that they get processed properly under all versions of Windows, remember to use AttachThreadInput.


Mark M. Baker is the Chief of Research & Development at BNA Software located in Washington, D.C. Do you have a Windows development question? Send it to [email protected].

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.