Question

I've been trying to make a simple program in C#, to launch different software, and move it to a specific screen, to be able to automatically set up different windows, on a machine with a total of 12 monitors.

Most of these windows is launched in either Chrome or Internet Explorer.

The code I use to move the appplications is the following:

[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

this.process = Process.Start(this.startInfo);
process.WaitForInputIdle();
SetForegroundWindow(this.process.MainWindowHandle);

Console.WriteLine("Process ID: "+ this.process.Handle);
this.process.Refresh();
Console.WriteLine("Main window handle: " + this.process.MainWindowHandle);

Point screenlocation = Screen.AllScreens[MonitorNum].Bounds.Location;
SetWindowPos(this.process.MainWindowHandle, -1, screenlocation.X, screenlocation.Y, Screen.AllScreens[MonitorNum].Bounds.Width, Screen.AllScreens[MonitorNum].Bounds.Height, 1);

It seems to be working just fine with Notepad, but when it's a browser MainWindowHandle always return IntPtr.Zero, even if I refresh the Process.

Any advice?

Was it helpful?

Solution

Modern browsers use complex multi-process architectures.

If a chrome process is already running when you launch a new chrome.exe process, then some Inter Process Communication takes place between the two processes and a new child process is launched (child of the old preexisting process) to host the new tab rendering. The process you launched then exit immediately, and there is no way you can grab a Main window for that now dead process. The new Chrome Main Window is created is the preexisting process.

You can experiment with the following C++ source

#include <Windows.h>
#include <stdio.h>

int main( void ) {

    STARTUPINFO SI;
    memset( &SI, 0, sizeof SI );
    SI.cb = sizeof SI;

    PROCESS_INFORMATION PI;

    BOOL bWin32Success =
    CreateProcess( L"C:\\Users\\manuel\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
                   NULL, NULL, NULL, FALSE, 0, NULL,
                   L"C:\\Users\\manuel\\AppData\\Local\\Google\\Chrome\\Application",
                   &SI, &PI );
    if ( bWin32Success ) {
        printf( "PID %u\n", PI.dwProcessId );
        DWORD dwRet = WaitForInputIdle( PI.hProcess, 1000 );
        switch ( dwRet ) {
            case 0:
                printf( "WaitForInputIdle succeedeed\n" );
                break;
            case WAIT_TIMEOUT:
                printf( "WaitForInputIdle timed out\n" );
                break;
            case WAIT_FAILED:
                printf( "WaitForInputIdle Error %u\n", GetLastError() );
                break;
            default:
                printf( "WaitForInputIdle Unknown return value %d\n", dwRet );
        }
        CloseHandle( PI.hThread );
        CloseHandle( PI.hProcess );

    } else {
        printf( "CreateProcess Error %u\n", GetLastError() );
    }
    return 0;
}

Using Spy++ and the Windows Task Manager, or better Process Explorer, you will see that, when chrome is already running, the new Chrome Main Window is hosted by the already running chrome.exe, and that the process launched by CreateProcess is terminated.

Solution:

  1. Take a snapshot of the currently displayed Chrome Main Window, using some Window Enumeration APIs
  2. Start a new chrome.exe
  3. Take a new snapshot. The new window is the window not present in the first snapshot.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top