Have a question? ask www.kenlet.com
Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  C# >  Single Instance Applications in WinForms
Add to MyHood
   Single Instance Applications in WinForms   [ printer friendly ]
Stats
  Rating: 4 out of 5 by 3 users
  Submitted: 07/07/03
Pedro Silva ()

 
In several different Windows programs that I've developed, I have needed to ensure that only one instance of that application was ever launched at once. Now, working with WinForms application, I ran across the same requirement. After looking through several of the .NET classes, I didn't find the APIs I was used to using in Win32 programming to do this, so I put together a couple of classes (SingleInstanceHelper and WindowsHelper to do just that.

The features of the single instance application are:
  • Create a new instance of the app if no other instances are running.
  • If another instance is already running, then bring its Window to the foreground.
  • When bringing the first instance to the foreground, restore minimized windows to their previous size.

WindowsHelper Code
The WindowsHelper class encapsulates several unmanaged Win32 APIs for dealing with window state and restoration. This class hides most of the complexity of the Win32 APIs and just exposes ActivateWindow method.

using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Win32APIs
{
    /// <summary>
    /// WindowsHelper implements managed wrappers for unmanaged Win32 APIs.
    /// </summary>
    public sealed class WindowsHelper
    {
        #region structures needed to call into unmanaged Win32 APIs.
        /// <summary>
        /// Point struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
            private class ManagedPt 
        {
            public int x = 0;
            public int y = 0;
            
            public ManagedPt() 
            {
            }

            public ManagedPt(int x, int y) 
            {
                this.x = x;
                this.y = y;
            }
        }

        /// <summary>
        /// Rect struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class ManagedRect 
        {
            public int x = 0;
            public int y = 0;
            public int right = 0;
            public int bottom = 0;
            
            public ManagedRect() 
            {
            }

            public ManagedRect(int x, int y, int right, int bottom) 
            {
                this.x = x;
                this.y = y;
                this.right = right;
                this.bottom = bottom;
            }
        }

        /// <summary>
        /// WindowPlacement struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class ManagedWindowPlacement 
        {
            public uint length = 0;
            public uint flags = 0;
            public uint showCmd = 0;
            public ManagedPt minPosition = null;
            public ManagedPt maxPosition = null;
            public ManagedRect normalPosition = null;
            
            public ManagedWindowPlacement() 
            {
                this.length = (uint)Marshal.SizeOf(this);
            }
        }
        #endregion

        // External Win32 APIs that we're calling directly.
        [DllImport("USER32.DLL", SetLastError=true)]
        private static extern uint ShowWindow (uint hwnd, int showCommand);
        
        [DllImport("USER32.DLL", SetLastError=true)]
        private static extern uint SetForegroundWindow (uint hwnd);
        
        [DllImport("USER32.DLL", SetLastError=true)]
        private static extern uint GetWindowPlacement (uint hwnd, 
            [In, Out]ManagedWindowPlacement lpwndpl);
        
        [DllImport("USER32.DLL", SetLastError=true)]
        private static extern uint FindWindow (string lpClassName, string lpWindowName);

        // Windows defined constants.
        private const int WM_ACTIVATE = 0x0006;
        private const int WA_CLICKACTIVE = 2;
        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;
        private const int SW_SHOWMAXIMIZED = 3;
        private const int WPF_RESTORETOMAXIMIZED = 2;

        /// <summary>
        /// Finds the specified window by its name (or caption). 
        /// </summary>
        /// <param name="windowName">Name of the window to find and activate.</param>
        /// <returns>Window handle or 0 if window not found by that name.</returns>
        static private uint FindWindow(string windowName)
        {
            // first, try to find the window by its window name or caption.
            uint hwndInstance = FindWindow(null, windowName);
            if (hwndInstance <= 0)
            {
                Debug.Assert(false, 
                    "Couldn't find window handle for the specified window name.");
            }

            return hwndInstance;
        }

        /// <summary>
        /// Finds the specified window by its name (or caption).  Then brings it to
        /// the foreground.
        /// </summary>
        /// <param name="windowName">Name of the window to find and activate.</param>
        static public void ActivateWindow(string windowName)
        {
            // first, try to find the window by its window name or caption.
            uint hwndInstance = FindWindow(windowName);

            // Then, get the WindowPlacement, so we can decide the best way to 
            // activate the window correctly.
            ManagedWindowPlacement placement = new ManagedWindowPlacement();
            GetWindowPlacement(hwndInstance, placement);

            if (placement.showCmd == SW_SHOWMINIMIZED)
            {
                // if the window is minimized, then we need to restore it to its
                // previous size.  we also take into account whether it was 
                // previously maximized.
                int showCmd = (placement.flags == WPF_RESTORETOMAXIMIZED) ? 
                    SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
                ShowWindow(hwndInstance, showCmd);
            }
            else
            {
                // if it's not minimized, then we just call SetForegroundWindow to 
                // bring it to the front.
                SetForegroundWindow(hwndInstance);
            }
        }
    }
}

The first item of interest in the DllImport keyword. DllImport is used to invoke unmanaged APIs that are not COM-based. Although I'm using it to call into a Win32 API, you can also use it to call any legacy C++ DLLs that you have built. For most of the external definitions, we're just passing around basic types like int and string. However, for the GetWindowPlacement method, Windows actually returns a struct with some information.

To account for the struct returned by the Win32 GetWindowPlacement call, I defined a set of managed classes that wrap the data in the corresponding unmanaged structs. As you can see, they contain the exact same public member variables. In addition, they tagged with the [StructLayout(LayoutKind.Sequential)] attribute. This is used to ensure that structured exactly as an unmanaged struct is layed out.

Finally, the ActivateWindow method locates the specified window by its Window name -- this is also known as its caption or text. The FindWindow Windows API also lets you search by window class name. However, WinForms auto-generates the class name (something like WindowsForms10.Window.8.app61), so it may be different between builds. I started trying to search by class name because it seemed like the right way to do it, but after a few different test runs, the class name for the form changed and you couldn't be assured what name to look for. So, I decided to go with the Window name instead.

After finding the corresponding Window handle, we check to see what the current state of the window is (normal, minimized, or maximized). If it's not currently minimized, then we just call SetForegroundWindow to bring the existing Window to the front. If it is minimized, then we check if its previous state was maximized. If it was, then we return the Window to its maximized state; otherwise, we restore it to its previous size and location.

SingleInstanceHelper Code
Now that we have the code for dealing with the native, unmanaged APIs that we need, it's time to look at the code that enforces the single instance nature of the application.
using System;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Win32APIs;

namespace SingleInstanceApp
{
    /// <summary>
    /// SingleInstanceHelper implements functionality to create single instance
    /// WinForms applications.
    /// </summary>
    public sealed class SingleInstanceHelper
    {
        // private member data.
        static private Mutex singleInstance = null;        // holds the mutex for this application.

        // private constants.
        private const string formatMutexName = "SingleInstanceMutex-{0}-{1}";

        /// <summary>
        /// Generates a unique identifier for this application's mutex based on its
        /// assembly name and version.
        /// </summary>
        static private string UniqueIdentifier
        {
            get
            {
                string appName = "";
                string appVersion = "0.0.0.0";
                Assembly entry = Assembly.GetEntryAssembly();

                if (entry != null) 
                {
                    AssemblyName assemblyName = entry.GetName();
                    if (assemblyName != null)
                    {
                        appName = assemblyName.Name;
                        appVersion = assemblyName.Version.ToString();
                    }
                }

                return string.Format(formatMutexName, appName, appVersion);
            }
        }

        /// <summary>
        /// Proxy method used by WinForm apps that want single instance behavior.
        /// This method will check if this is the first instance and create a new form of the
        /// specified type.  Otherwise, it will find the currently running instance and bring it
        /// to the foreground.
        /// </summary>
        /// <param name="newFormType">Type of the form to create if this is the first instance.</param>
        static public void ProxyMain(Type newFormType, string windowName)
        {
            // first, we create an instance of the Mutex with the appropriate identifier
            // for this app.
            singleInstance = new Mutex(false, UniqueIdentifier);

            // then, we attempt to lock the Mutex to see if its the first intance.
            // a Mutex can only ever have one lock on it at a time.
            if (singleInstance.WaitOne(1, true)) 
            {
                // if this is the first instance, then create an instance of the main Form.
                // we use the specified type and reflection to call the default constructor 
                // for the Form using Reflection.  That way this class is reuseable for any
                // applications you'd like to use it on.
                ConstructorInfo constructor = newFormType.GetConstructor(Type.EmptyTypes);
                Form newForm = (Form)constructor.Invoke(null);
                Application.Run(newForm);
            }
            else
            {
                // if its not the first instance, then bring the first one to the foreground.
                WindowsHelper.ActivateWindow(windowName);
            }
        }
    }
}

The bulk of the interesting work is in the ProxyMain method. This method uses a Mutex -- a mutually exclusive semaphore -- to ensure that only one instance of the app is ever created. The first thing it does is attempt to lock the Mutex class by calling the WaitOne method. Notice that this doesn't actually wait at all. If it can't get the lock right away, that means there's a running instance and we return right away to continue with the second option of activating the existing application instance. If it can get a lock, then this must be the first instance, so the main Form for the application is created and the Application.Run method is called -- this is what usually happens in a typical WinForm app's Main method. Also, to make this code more flexible and reusable as a library by multiple programs, the instance of the new form is created using Reflection based on the Type that's passed into this method.

Finally, the UniqueIdentifier property is used to calculate a unique name to use for the Mutex. The name unique for different applications and versions, but otherwise will have the same name. This ensures that the ProxyMain method is looking for the right Mutex for the current app. We get the application name and version number from the Assembly information for this program.

SingleInstanceApp Code
The follow class shows the code changes needed in a simple Form class to make it behave as a single instance application.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace SingleInstanceApp
{
    /// <summary>
    /// Summary description for FormSingleInstanceApp.
    /// </summary>
    public class FormSingleInstanceApp : System.Windows.Forms.Form
    {
        // private constants;
        private const string windowName = "Single Instance App";

        // Windows Form Designer generated member data.
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label labelCurrentProcess;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public FormSingleInstanceApp()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        // Windows Form Designer generated code removed for brevity...

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args) 
        {
            SingleInstanceHelper.ProxyMain(typeof(FormSingleInstanceApp), windowName);
        }

        private void FormSingleInstanceApp_Load(object sender, System.EventArgs e)
        {
            Process currentProcess = Process.GetCurrentProcess();
            if (currentProcess != null)
                this.labelCurrentProcess.Text = currentProcess.Id.ToString();
        }
    }
}

The SingleInstanceHelper class is really easy to integrate into the Form code. Basically, you just need to replace the code in the typical Main method with the call to ProxyMain, passing it type of Form you want to create and the window name to look for.

The rest of the typical is just the stuff you need to create a WinForm. I also put in some code in OnLoad to help in debugging by showing the process id of the current running process. This shows that there's ever only one instance and it's the same instance that was first started.

Conclusion
There are many Windows applications that have found it useful to only allow a single instance to ever run. Now, with the code in this tutorial, you can see how that same type of application can be written on the .NET Framework. And, the classes provided in the tutorial can be used as is to add directly into your own application to enable that behavior.

Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
I think the above task (i.e. Running single instance of application and reactivating the previously running instance if it is already running) with following code (ofcourse the code is in Visual Basic, but can be converted to C#)

'''Applicable to VB.NET Windows applications
Dim objProcess As Process 'Variable to hold individual Process
Dim objProcesses() As Process 'Collection of all the Processes running on local machine

objProcesses = Process.GetProcesses() ''Get all processes into the collection
For Each objProcess In objProcesses
'Check and exit if we have SMS running already
'APPLICATION1 is the TEXT specified to the STARTUP form
If objProcess.MainWindowTitle = "APPLICATION1" Then
MsgBox("Another instance of APPLICATION1 is already running")
AppActivate(objProcess.Id) ''Activate the running process
End
End If
Next


Please try the above code give any feedback..

Regards
Raj Chidipudi
-- Raj Chidipudi, August 04, 2003
 
I have converted the code to C#


//get the current process
Process oCurrentProcess = Process.GetCurrentProcess();

//get all other process by the same name, atleast one will be returned - the current one.
Process[] arrProcesses = Process.GetProcessesByName(oCurrentProcess.ProcessName);

bool bProcess = false;
//go thru all of them
foreach(Process oProcess in arrProcesses)
{
//if the id is different, that means another process by the same name exists
if(oProcess.Id != oCurrentProcess.Id) //
{
MessageBox.Show("Another instance of the application is already running");
bProcess = true;
break;
}
}

if(!bProcess)
{
//if process not found, start one
Application.Run(new Form1());
}
else
{
//else exit
Application.Exit();
}
-- Nimesh Gala, January 08, 2004
 
A better way of finding the process:

Process proc = Process.GetProcessesByName(Process.GetCurrentProcess.ProcessName)(0);

(That's assuming only one instance *ever* runs ;)
-- Ernst Kuschke, June 25, 2004
 
Anyone know how to be able to make a method call on the single instance once it is called again?
-- Andres Sosa, July 26, 2004
 
static private uint FindWindow(string windowName)
{
// first, try to find the window by its window name or caption.
uint hwndInstance = FindWindow(null, windowName);
if (hwndInstance <= 0)
{
Debug.Assert(false,
"Couldn't find window handle for the specified window name.");
}

return hwndInstance;
}

This can be done a little smarter.

static private uint FindWindow(string windowName)
{
// first, try to find the window by its window name or caption.
uint hwndInstance = FindWindow(null, windowName);

Debug.Assert(hwndInstance > 0,
"Couldn't find window handle for the specified window name.");

return hwndInstance;
}
-- Willem Meints, March 10, 2005
 
This article has gotten some good feedback and review and personal messages to me, so it's obviously a need that many people have. In Whidbey, this functionality is actually built into part of the Visual Basic libraries. But, we can also easily use it from C#.

Here's a pointer to a new article that I recently wrote on Single Instance applications in Whidbey:
http://blogs.msdn.com/pedrosilva/archive/2005/03/09/391381.aspx
(for some reason, it wouldn't let me put a link tag into this comment)

Enjoy.
-- Pedro Silva, March 17, 2005
 
Copyright © 2001 DevHood® All Rights Reserved