Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Launch a .exe application - not Notepad - inside a Panel in a WinForm using EnumWindows

I need to launch some Windows apps to run inside a form. I'm developing a Windows desktop app on C# using VS-15 IDE.

This topic has been tackled in SO many years ago. This is the best answer I found:

How can I run another application within a panel of my C# program?

However all examples deal with launching Notepad, which works ok. I indeed can launch and run notepad inside a target panel in a target form, and works perfectly.

But I don't need to launch Notepad, I need to launch other installed apps, and have found the frustrating reality that's impossible to launch the desired app inside the target panel/form. It just opens outside the form.

Even found a post in which a OP needs to launch IE, but the same result, IE starts outside the form.

Reading and reading related topics, I've found that it could be a probability of being solved using something called EnumWindows. I'm really new to C#, I usually deal with Python. I'm not even native to windows, had to install it in a VM.

I'm using Visual Studio 2015. The expected output is a desktop app to Windows 7 and 8 machines, 64 bits.

The code:

using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace < >
{
public partial class Form1: 
  {

   private void btn_qui_Click(object sender, EventArgs e)

     {
        var postventa = new appUser("POSTVENTA", "",3);

        try
        {
            frm_3.Show();
            Process p = Process.Start("C://QuiterWeb/QuiterWeb.exe");
            p.WaitForInputIdle();
            SetParent(p.MainWindowHandle, frm_3.panel_3.Handle);

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
  }

}

When running the code, the .exe opens outside the form. But if instead I make it open notepad.exe, it opens inside the specific panel and form. Which indicates that the destination is well mapped to the method.

What is wrong here? How to make the .exe run inside the panel and form?

like image 761
digitai Avatar asked Mar 14 '23 00:03

digitai


2 Answers

It depends on your specific process. It may not have a main window handle when it's idle (it's not common, but it may happen), or it could have several "main windows".

The correct way to wait for a process to have its first MainWindowHandle would be something like:

Process p = Process.Start("C://QuiterWeb/QuiterWeb.exe");
p.WaitForInputIdle();
while (p.MainWindowHandle == IntPtr.Zero)
{
   Thread.Sleep(100); // Don't hog the CPU
   p.Refresh(); // You need this since `MainWindowHandle` is cached

   // do additional checks, or add a timeout in case the process is stalled
   // or never creates a main window handle, etc.
}
SetParent(p.MainWindowHandle, frm_3.panel_3.Handle);

If your process actually spawns more than one "main window", then you'll need to tamper it (waiting for the second MainWindowHandle)

As for your specific question, EnumWindows just lists the windows the process has spawned... this could be useful if your process has more than one window and you want to host them all within winforms, but it'd be pretty much process-specific (you'll more likely also need EnumChildWindows or EnumThreadWindows and enumerate on all processes to make something generic, plus a specific need which would need a specific UI if you have the need to host the windows within your own).

like image 100
Jcl Avatar answered Apr 06 '23 11:04

Jcl


Try adding a sleep after the call to WaitForInputIdle method.

sample:

[DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 

        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

        [DllImport("user32.dll")]
        static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);

        static readonly int GWL_STYLE = -16;
        static readonly int WS_VISIBLE = 0x10000000;

        private void button1_Click(object sender, EventArgs e)
        {
            Process p = Process.Start(@"C:\test\SampleWinForm2.exe");
            p.WaitForInputIdle();
            Thread.Sleep(3000); //sleep for 3 seconds
            SetParent(p.MainWindowHandle, panel1.Handle);
            SetWindowLong(p.MainWindowHandle, GWL_STYLE, WS_VISIBLE);
            MoveWindow(p.MainWindowHandle, 0, 0, panel1.Width, panel1.Height, true);
        }
like image 29
bkdev Avatar answered Apr 06 '23 10:04

bkdev