1

I have a Win Forms app in which I am starting a process from a different .exe file, then setting it's Handle property to a panel in my Win Form app. This gives the effect of the other app running inside of the Win Forms app.

Here is how I accomplish that:

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hwc, IntPtr hwp);

string exepath = "myProgram.exe";
ProcessStartInfo p = new ProcessStartInfo(exepath);
process = Process.Start(p);
Thread.Sleep(500);  //sleep to allow program to start up properly        
SetParent(process.MainWindowHandle, pictureBox1.Handle); //then set the handle to give the effect of being run inside the win forms app

Now I know I can call process.Kill() in a FormClosed event, which terminates the process when this form is closed, but how would I go about killing the process if my Win Form app is forcefully quit? Is that even possible?

Because the process's handle is set to a panel in the Win Form app, it doesn't appear on the taskbar but it will still continue to run if process.Kill() is not called, which happens when the Win Form is forcefully closed. This means each time I have to shut it off via task manager, which is a pain..

If this is not possible, I will not bother with setting the Handle to the panel, and I will just have it open in a new window.

Thanks

user2943464
  • 57
  • 1
  • 8

2 Answers2

1

Try with this code, I was using it for a long time:

public partial class Launcher : Form
{
    /// <summary>Collection of process. </summary>
    private Dictionary<IntPtr, Process> _processCollection = new Dictionary<IntPtr, Process>();

    #region DLL Import


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


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

    #endregion


    /// <summary>Default constructor. </summary>
    public Launcher()
    {
        InitializeComponent();

        try {
            FormClosing += Launcher_FormClosing;
            StartInstances();
        }
        catch (Exception ex) {
            MessageBox.Show(ex.Message);
        }
    }

    /// <summary>Starts the instances. </summary>
    private void StartInstances()
    {
        var path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
        var numberOfInstances = Int32.Parse(ConfigurationManager.AppSettings["NumerOfInstances"]);
        for (int i = 0; i < numberOfInstances; i++) {
            StartInstance(i, path);
        }
    }

    /// <summary>Starts an instance. </summary>
    private void StartInstance(int instanceId, string path)
    {
        Process proc = Process.Start(path + "\\foo.exe", instanceId.ToString());
        IntPtr handlerDocked = IntPtr.Zero;

        Panel panel = new Panel();
        panel.Size = new Size(flwPanel.Width / 3, flwPanel.Height / 2);
        flwPanel.Controls.Add(panel);

        do {
            try {
                proc.WaitForInputIdle(1000); //wait for the window to be ready for input;
                proc.Refresh();              //update process info
                if (proc.HasExited) {
                    return; //abort if the process finished before we got a handle.
                }
                handlerDocked = proc.MainWindowHandle;  //cache the window handle
            }
            catch {
                Thread.Sleep(500);
            }

        } while (handlerDocked == IntPtr.Zero);

        //hWndOriginalParent = SetParent(hWndDocked, panel1.Handle);
        SetParent(handlerDocked, panel.Handle);

        var docked = new DockedElement(handlerDocked, panel);

        panel.SizeChanged += new EventHandler(Panel_Resize);
        Panel_Resize(docked, new EventArgs());

        _processCollection.Add(handlerDocked, proc);
    }
    private void Panel_Resize(object sender, EventArgs e)
    {
        var docked = (DockedElement)sender;
        //Change the docked windows size to match its parent's size. 
        MoveWindow(docked.Handle, 0, 0, docked.Container.Width, docked.Container.Height, true);
    }

    /// <summary>Finallize instances. </summary>
    public void FinallizeInstances()
    {

        foreach (var docked in _processCollection) {
            docked.Value.Close();
        }
        _processCollection.Clear();
    }

    private void Launcher_FormClosing(object sender, FormClosingEventArgs e)
    {
        FinallizeInstances();
    }

    protected override void Dispose(bool disposing)
    {
        FinallizeInstances();

        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
}
Óscar Andreu
  • 1,630
  • 13
  • 32
0

To be sure your child processes are properly wiped as soon as your application closes (gracefully or forcefully), you can use Job Objects.

A job object allows groups of processes to be managed as a unit. Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. Operations performed on a job object affect all processes associated with the job object. Examples include enforcing limits such as working set size and process priority or terminating all processes associated with a job.

There is a similar question on StackOverflow with a detailed implementation in C#: Working example of CreateJobObject/SetInformationJobObject pinvoke in .net?

Community
  • 1
  • 1
Jämes
  • 6,945
  • 4
  • 40
  • 56