In my GUI application, I'm using the C# Process
class to spawn external processes which may launch windows. The subprocess windows may be displayed via third-party API calls, so it's not always possible to get the window handle. Is there any way to ensure that the subprocess's windows are displayed in front of the main application window?
The usual method is:
1 . Get Process class instance returned by Process.Start()
2 . Query Process.MainWindowHandle
3 . Call unmanaged Win32 API function "ShowWindow" or "SwitchToThisWindow"
The trick to your question is that "sub-process windows may be displayed via third-party API calls". In that case you will need Get the Window Handle of the spawned exe and Enum Child Windows. Once you have the Handles for the forms that are shown after API calls you can use the BringWindowToTop API.
I put together a small test using How To Enumerate Windows Using the WIN32 API as inspiration. Create a Windows application with 1 form and 2 buttons:
public partial class Form1 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public Form1()
{
InitializeComponent();
}
private System.IntPtr hWnd;
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start(@"C:\TFS\Sandbox\3rdPartyAppExample.exe");
try
{
do
{
p.Refresh();
}
while (p.MainWindowHandle.ToInt32() == 0);
hWnd = new IntPtr(p.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
3rdPartyAppExample.Form1 f = new 3rdPartyAppExample.Form1();
f.ShowForm2();
//Bring main external exe window to front
BringWindowToTop(hWnd);
//Bring child external exe windows to front
BringExternalExeChildWindowsToFront(hWnd);
}
private void BringExternalExeChildWindowsToFront(IntPtr parent)
{
List<IntPtr> childWindows = GetChildWindows(hWnd);
foreach (IntPtr childWindow in childWindows)
{
BringWindowToTop(childWindow);
}
}
// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</param>
/// <returns>List of child windows</returns>
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
}
The 3rdPartyAppExample is a Winform App with 2 forms. I reference this application and call Form1's Public method to show Form2:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void ShowForm2()
{
var f = new Form2();
f.Show();
}
Optionally you may wish to check the Windows Caption:
hInst = ProcessStart("calc.exe")
// Begin search for handle
hWndApp = GetWinHandle(hInst)
If hWndApp <> 0 Then
// Init buffer
buffer = Space$(128)
// Get caption of window
numChars = GetWindowText(hWndApp, buffer, Len(buffer))
The other solution (that isn't very stable) is discussed here: http://www.shloemi.com/2012/09/solved-setforegroundwindow-win32-api-not-always-works/
The trick is to make windows ‘think’ that our process and the target window (hwnd) are related by attaching the threads (using AttachThreadInput API).
Regarding the myTopForm.TopMost = true
answer, that will not work for external applications.
It has nothing to do with the "process" and everything to do with Windows focus.
You might be able to do something like this:
http://msdn.microsoft.com/en-us/library/3saxwsad.aspx
public void MakeOnTop()
{
myTopForm.TopMost = true;
}
But in general, you'd need the Window handle (the spawned process can figure out its own handle easily enough) and you'd need something like this:
http://support.microsoft.com/kb/186431
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With