1

I need to open a website in the default browser. I then want to display the browser on the second screen. Therefore I try the following:

private void button1_Click(object sender, EventArgs e)
{
    var externalApplication = new Process();
    externalApplication.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
    externalApplication.StartInfo.FileName = "http://www.example.com/";
    externalApplication.Start();
    externalApplication.WaitForInputIdle();

    var handle = externalApplication.MainWindowHandle;
    Console.Write(handle);
    Program.MoveWindow(handle, 0, 0, 1500, 1, true);
}

[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

Now my problem is that neither WaitForInputIdle() nor MainWindowHandle() seem to work here. They both throw an InvalidOperationException: No process is associated with this object

Ole Albers
  • 8,715
  • 10
  • 73
  • 166
  • Does [this](https://social.msdn.microsoft.com/Forums/en-US/79b6be7e-5f00-474c-a485-50db92feefe2/no-process-is-associated-with-this-object-in-waitforinputidle-and-movewindow-for-audio-files?forum=csharpgeneral) help you? – diiN__________ Nov 16 '16 at 09:02

1 Answers1

0

I solved this by getting the Parent-Handle using the answer from @Simon Mourier to get the parent

and changing the code that way:

private void OpenUrl(string url)
{
    var oldProcessIds = Process.GetProcesses().Select(pr => pr.Id);

    IntPtr handle = OpenApplication(url);
    if (handle==IntPtr.Zero)
    {
        // Find out what Process is new
        var processes = Process.GetProcesses();
        var newProcess = processes.Where(pr => !oldProcessIds.Contains(pr.Id));
        var parent = ParentProcessUtilities.GetParentProcess(newProcess.First().Handle);
        Program.MoveWindow(parent.MainWindowHandle, 0, 500, 500, 300, true);
    }
    else
    {
        Program.MoveWindow(handle, 0, 0, 1500, 100, true);
    }
}

private IntPtr OpenApplication(string application)
{
    var externalApplication = new Process();
    externalApplication.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
    externalApplication.StartInfo.FileName = "http://www.example.com";
    externalApplication.Start();    
    externalApplication.Refresh();
    try
    {
        externalApplication.WaitForInputIdle();
        return externalApplication.MainWindowHandle;
    } catch 
    {
        // Cannot get Handle. Application managaes multiple Threads
        return IntPtr.Zero;
    }
}

Code to retrieve the parent:

 [StructLayout(LayoutKind.Sequential)]
    public struct ParentProcessUtilities
    {
        // These members must match PROCESS_BASIC_INFORMATION
        internal IntPtr Reserved1;
        internal IntPtr PebBaseAddress;
        internal IntPtr Reserved2_0;
        internal IntPtr Reserved2_1;
        internal IntPtr UniqueProcessId;
        internal IntPtr InheritedFromUniqueProcessId;

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);

        /// <summary>
        /// Gets the parent process of the current process.
        /// </summary>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess()
        {
            return GetParentProcess(Process.GetCurrentProcess().Handle);
        }

        /// <summary>
        /// Gets the parent process of specified process.
        /// </summary>
        /// <param name="id">The process id.</param>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess(int id)
        {
            Process process = Process.GetProcessById(id);
            return GetParentProcess(process.Handle);
        }

        /// <summary>
        /// Gets the parent process of a specified process.
        /// </summary>
        /// <param name="handle">The process handle.</param>
        /// <returns>An instance of the Process class or null if an error occurred.</returns>
        public static Process GetParentProcess(IntPtr handle)
        {
            ParentProcessUtilities pbi = new ParentProcessUtilities();
            int returnLength;
            int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
            if (status != 0)
                return null;

            try
            {
                return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
            }
            catch (ArgumentException)
            {
                // not found
                return null;
            }
        }

Explanation:

Before opening the URL the program remembers what processes existed before. So after creation the new process can be grabbed. From the Process the parent will be searched, which should be the main Browser process. The MainWindowHandle of that Process then is taken to move the window.

Note:

This is a "rough" first solution (more like a POC) until now. There are missing error handling routines and it optimistically assumes that no other process gets started in the same time.

Community
  • 1
  • 1
Ole Albers
  • 8,715
  • 10
  • 73
  • 166