Click here to Skip to main content
15,898,036 members
Articles / Programming Languages / C#

Hosting EXE Applications in a WinForm Project

Rate me:
Please Sign up or sign in to vote.
4.70/5 (57 votes)
21 Dec 20041 min read 597.5K   27.3K   174   123
A custom control to launch and embed an EXE into a WinForm based application
In this post, you will learn about a custom C# control that allows you to specify the name of an executable you want embedded into your application.

Sample Image

Introduction

Though not a common task, recently I needed to take an existing executable application and embed it into an application I was building. Oddly enough, I did not need any interaction between my application and the existing EXE. As it ends up, this is not a difficult thing to do. To make it even easier, I created a custom C# control that allows you to specify the name of an executable you want embedded into your application. The control takes care of all the rest.

How Does It Work

In design time, the user can specify the name of the executable to embed. When the control is created in runtime, it launches the application as follows:

C#
Process p = null; 
try 
{
  // Start the process 
  p = System.Diagnostics.Process.Start(this.exeName); 

  // Wait for process to be created and enter idle condition 
  p.WaitForInputIdle(); 

  // Get the main handle
  appWin = p.MainWindowHandle; 
} 
catch (Exception ex) 
{ 
  MessageBox.Show(this, ex.Message, "Error"); 
}

After launching, the code must then set the parent of the executable's main window to the control handle. It does this as follows:

C#
// Put it into this form
SetParent(appWin, this.Handle);

// Remove border and whatnot
SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE);

// Move the window to overlay it on this window
MoveWindow(appWin, 0, 0, this.Width, this.Height, true);

Any time the control is resized, it must also resize the executable window. To do so, it does this in the Resize function:

C#
protected override void OnResize(EventArgs e)
{
  if (this.appWin != IntPtr.Zero)
  {
    MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
  }
  base.OnResize (e);
}

Lastly, when the control is destroyed, it should shut down the executable. To do so, it does the following:

C#
protected override void OnHandleDestroyed(EventArgs e)
{
  // Stop the application
  if (appWin != IntPtr.Zero)
  {
    // Post a colse message
    PostMessage(appWin, WM_CLOSE, 0, 0);

    // Delay for it to get the message
    System.Threading.Thread.Sleep(1000);

    // Clear internal handle
    appWin = IntPtr.Zero;
  }

  base.OnHandleDestroyed (e);
}

History

  • 30th December, 2004: Initial release

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Architect Omron Adept Technologies, Inc
United States United States
I have been developing software professionaly since 1991 writing software in automation and manufacturing environments. For 14 years I worked for companies that built custom robotic automated equipment for the semiconductor, telecommunications, and other industies. Presently, I work for a company that manufacturers industrial robots where I write high level coordination and control software.

My undergraduate degrees are in Mathematics and Philosopy. My graduate degree is in Management Information Systems. I am MCSD certified in Visual C++ 6.0 and MCSD.NET certified in C#. I am also have the PMI-ACP certification.

I enjoy karate and reading.

Comments and Discussions

 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
rimblock10-Jun-07 12:26
rimblock10-Jun-07 12:26 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
Elbe Alves Miranda24-Feb-11 3:18
Elbe Alves Miranda24-Feb-11 3:18 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
hrdpatel23-Jul-07 6:30
hrdpatel23-Jul-07 6:30 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
mcaos11-Aug-07 4:12
professionalmcaos11-Aug-07 4:12 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
sagyla31-Aug-07 13:19
sagyla31-Aug-07 13:19 
GeneralRe: A call to PInvoke function 'AppControl!AppControl.ApplicationControl::SetWindowLong' Pin
RoyalRikyoo25-Feb-09 21:08
RoyalRikyoo25-Feb-09 21:08 
Generalcan't start programe that have login window. Pin
caixrz11-Apr-07 16:56
caixrz11-Apr-07 16:56 
AnswerRe: can't start programe that have login window. Pin
Member 899490321-May-12 3:32
Member 899490321-May-12 3:32 
Great post, thanks everybody. Smile | :)

I added a public property exeParametros and used it in the process start method to send the parameters. It works opening a file with the notepad.

p = System.Diagnostics.Process.Start(this.exeName, exeParametros);
I attach the modified code:

C#
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ExeEmbedder
{

    /// <summary>
    /// Application Display Control
    /// </summary>
    [
    ToolboxBitmap(typeof(ApplicationControl), "AppControl.bmp"),
    ]
    public class ApplicationControl : System.Windows.Forms.Panel
    {

        /// <summary>
        /// Track if the application has been created
        /// </summary>
        bool created = false;

        /// <summary>
        /// Handle to the application Window
        /// </summary>
        IntPtr appWin;

        /// <summary>
        /// The name of the exe to launch
        /// </summary>
        private string exeName = "";

        /// <summary>
        /// Get/Set if we draw the tick marks
        /// </summary>
        [
        Category("Data"),
        Description("Name of the executable to launch"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
        ]
        public string ExeName
        {
            get
            {
                return exeName;
            }
            set
            {
                exeName = value;
            }
        }

        private string exeParametros = "";
        /// <summary>
        /// Get/Set if we draw the tick marks
        /// </summary>
        [
        Category("Data"),
        Description("String para pasar al ejecutable"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
        ]
        public string ExeParametros
        {
            get
            {
                return exeParametros;
            }
            set
            {
                exeParametros = value;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public ApplicationControl()
        {
        }


        [DllImport("user32.dll", EntryPoint="GetWindowThreadProcessId",  SetLastError=true,
             CharSet=CharSet.Unicode, ExactSpelling=true,
             CallingConvention=CallingConvention.StdCall)]
        private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);

        [DllImport("user32.dll", SetLastError=true)]
        private static extern IntPtr FindWindow (string lpClassName, string lpWindowName);

        [DllImport("user32.dll", SetLastError=true)]
        private static extern long SetParent (IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32.dll", EntryPoint="GetWindowLongA", SetLastError=true)]
        private static extern long GetWindowLong (IntPtr hwnd, int nIndex);

        [DllImport("user32.dll", EntryPoint="SetWindowLongA", SetLastError=true)]
        private static extern long SetWindowLong (IntPtr hwnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll", SetLastError=true)]
        private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);

        [DllImport("user32.dll", SetLastError=true)]
        private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

        [DllImport("user32.dll", EntryPoint="PostMessageA", SetLastError=true)]
        private static extern bool PostMessage(IntPtr hwnd, uint Msg, long wParam, long lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsWindowVisible(IntPtr hWnd);


        private const int SWP_NOOWNERZORDER = 0x200;
        private const int SWP_NOREDRAW = 0x8;
        private const int SWP_NOZORDER = 0x4;
        private const int SWP_SHOWWINDOW = 0x0040;
        private const int WS_EX_MDICHILD = 0x40;
        private const int SWP_FRAMECHANGED = 0x20;
        private const int SWP_NOACTIVATE = 0x10;
        private const int SWP_ASYNCWINDOWPOS = 0x4000;
        private const int SWP_NOMOVE = 0x2;
        private const int SWP_NOSIZE = 0x1;
        private const int GWL_STYLE = (-16);
        private const int WS_VISIBLE = 0x10000000;
        private const int WM_CLOSE = 0x10;
        private const int WS_CHILD = 0x40000000;

        /// <summary>
        /// Force redraw of control when size changes
        /// </summary>
        /// <param name="e">Not used</param>
        protected override void OnSizeChanged(EventArgs e)
        {
            this.Invalidate();
            base.OnSizeChanged (e);
        }


        /// <summary>
        /// Creeate control when visibility changes
        /// </summary>
        /// <param name="e">Not used</param>
        protected override void OnVisibleChanged(EventArgs e)
        {

            // If control needs to be initialized/created
            if (created == false)
            {

                // Mark that control is created
                created = true;

                // Initialize handle value to invalid
                appWin = IntPtr.Zero;

                // Start the remote application
                Process p = null;
                try
                {
                    // Start the process

                    p = System.Diagnostics.Process.Start(this.exeName, exeParametros);

                    // Wait for process to be created and enter idle condition
                    //System.Threading.Thread.Sleep(1000);
                    while (p.MainWindowHandle == IntPtr.Zero || !IsWindowVisible(p.MainWindowHandle))
                    {
                        System.Threading.Thread.Sleep(10);
                        p.Refresh();
                    }

                    // Wait for process to be created and enter idle condition
                    p.WaitForInputIdle();

                    // Get the main handle
                    appWin = p.MainWindowHandle;

                }
                catch (Exception ex)
                {
                    MessageBox.Show(this, ex.Message, "Error");
                }

                // Put it into this form
                SetParent(appWin, this.Handle);

                // Remove border and whatnot
                SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE);

                // Move the window to overlay it on this window
                MoveWindow(appWin, 0, 0, this.Width, this.Height, true);

            }

            base.OnVisibleChanged (e);
        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="e"></param>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            // Stop the application
            if (appWin != IntPtr.Zero)
            {

                // Post a colse message
                PostMessage(appWin, WM_CLOSE, 0, 0);

                // Delay for it to get the message
                System.Threading.Thread.Sleep(1000);

                // Clear internal handle
                appWin = IntPtr.Zero;

            }

            base.OnHandleDestroyed (e);
        }


        /// <summary>
        /// Update display of the executable
        /// </summary>
        /// <param name="e">Not used</param>
        protected override void OnResize(EventArgs e)
        {
            if (this.appWin != IntPtr.Zero)
            {
                MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
            }
            base.OnResize (e);
        }


    }


}

QuestionHosting exe in Com application Pin
Vismay_Dhonsale10-Apr-07 4:40
Vismay_Dhonsale10-Apr-07 4:40 
GeneralError "this.Controls.Add(this.applicationControl1);" Pin
Sheikko8-Jan-07 23:50
Sheikko8-Jan-07 23:50 
AnswerRe: Error "this.Controls.Add(this.applicationControl1);" Pin
Elric-Wang25-Feb-07 14:27
Elric-Wang25-Feb-07 14:27 
GeneralRe: Error "this.Controls.Add(this.applicationControl1);" Pin
dontknow6115-Apr-07 12:28
dontknow6115-Apr-07 12:28 
GeneralRe: Error "this.Controls.Add(this.applicationControl1);" Pin
supermankelly31-Jul-08 3:37
supermankelly31-Jul-08 3:37 
GeneralAppControle class rewrite for vb.net Pin
madbison28-Jul-06 12:10
madbison28-Jul-06 12:10 
Generalget the current directory of hosted iexplore Pin
malai kofta9-Oct-05 10:13
malai kofta9-Oct-05 10:13 
Generalerror Pin
malai kofta11-Aug-05 9:47
malai kofta11-Aug-05 9:47 
GeneralRe: error Pin
malai kofta9-Oct-05 1:25
malai kofta9-Oct-05 1:25 
GeneralRe: error Pin
malai kofta9-Oct-05 10:12
malai kofta9-Oct-05 10:12 
GeneralVB Exe problem Pin
John Bizios25-Feb-05 8:31
John Bizios25-Feb-05 8:31 
GeneralRe: VB Exe problem Pin
dgsconseil14-Mar-05 6:15
dgsconseil14-Mar-05 6:15 
GeneralRe: VB Exe problem Pin
John Bizios14-Mar-05 7:02
John Bizios14-Mar-05 7:02 
GeneralRe: VB Exe problem Pin
dgsconseil16-Apr-05 23:19
dgsconseil16-Apr-05 23:19 
Questionhow? Pin
5sxz73410-Jan-05 0:34
5sxz73410-Jan-05 0:34 
Generalembed console application Pin
tlc66031-Dec-04 4:08
tlc66031-Dec-04 4:08 
GeneralI was excited, but... Pin
wyx200030-Dec-04 10:01
wyx200030-Dec-04 10:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.