Click here to Skip to main content
16,017,852 members
Articles / Programming Languages / C#
Article

Enforcing Single Instance of a Hidden-Window Application

Rate me:
Please Sign up or sign in to vote.
4.44/5 (14 votes)
17 Nov 2006CPOL3 min read 80.5K   805   35   20
Yet another article on single instance, but with a twist.

Introduction

Single instance application. There may not be a topic on which there are more good quality articles, so why another? All the methods I could find rely on the main window of the first instance being visible, which won't be the case if you have an application that hides its main window, such as a system tray application. Not only does this method work in the case of a hidden window, but compares favorably for the general case too.

Background

It turns out that detecting whether your application is already running is not very hard. There are several Win32 synchronization objects that will help (this project uses events), or you can enumerate windows and search their titles (this won't work if the window is hidden though). On the other hand, it is difficult to get the first instance of the application to display its window. Especially if the window is hidden, any method using messages or manipulating the window handle won't work.

Here are some other articles you might find helpful. I'm kind of partial to this one, which also has some code for displaying an animation as the application minimizes to the sys tray area of the task bar (which, by the way, is apparently unnecessary in XP): A Single Instance Application which Minimizes to the System Tray when Closed.

Here's another recent one that has the added advantage of having no unmanaged code (unlike my example): Single Instance Application, Passing Command Line Arguments.

Using the code

The method is simple, but the implementation a little less so. This is how it's done: the application attempts to open a named event. If the event exists, then the application knows it is the second instance. In that case, it signals the event and exits. If the named event doesn't exist, then the application creates it and starts a thread which waits for the event to be signaled. If the event becomes signaled, it uses P/Invoke to call a delegate on the GUI thread. The delegate shows the window, then calls a Win32 function to bring itself to the foreground.

The code that creates and checks the event is in a class called Event. The app's Main will create an Event object and use it to check whether another instance is already running. Here's what the Main looks like:

C#
static void Main() 
{
    // this class provides single instance feature
    evnt = new Event();
    
    // already exists means another instance is running
    if ( evnt.EventAlreadyExists() )
    {
        // signal the other instance to come to foreground
        evnt.SignalEvent();
    }
    else
    {
        // otherwise start normally
        SingleInstanceForm f = new SingleInstanceForm();
        // important: access the Handle so .net will create it
        IntPtr dummy = f.Handle;
        f.eventSignalled = 
           new EventSignalledHandler( f.evnt_EventSignalled );
        evnt.SetObject( f );

        Application.Run();
    }

}

Notice that I don't call Application.Run() with the form reference, which allows this application to start without showing a window. While this method doesn't need a visible window, P/Invoke does need a valid window handle. Accessing the form's handle forces .NET to create it for us.

Here's what the Event constructor looks like:

C#
public Event()
{
    eventHandle = OpenEvent( EVENT_MODIFY_STATE | 
                  SYNCHRONIZE, false, EVENT_NAME );
    if ( eventHandle == IntPtr.Zero )
    {
        eventHandle = CreateEvent( IntPtr.Zero, true, 
                      false, EVENT_NAME );
        if ( eventHandle != IntPtr.Zero )
        {
            Thread thread = 
               new Thread( new ThreadStart( WaitForSignal ) );
            thread.Start();
        }
    }
    else
    {
        eventAlreadyExists = true;
    }

}

OpenEvent will return a valid handle only if another instance has created it. So, it's very important that the handle gets closed, which is why I implement the IDisposable interface. If the handle were left open, your app would run the first time, but would not be able to start again until after a reboot or logout.

Win32 Synchronization object names can be defined on a per session or a per machine basis. This project prepends "Local\" to the name, which means it is only unique for the currently logged in user.

Points of interest

I learned about implementing IDisposable from this article: Implementing IDisposable and the Dispose Pattern Properly.

One of the keys to this project is calling a method on a GUI thread from a worker thread, and in .NET, this is handled by P/Invoke. There are many good articles on CodeProject about P/Invoke, but I also want to point out a great reference site: http://www.pinvoke.net/.

I look forward to hearing comments and better or more clear ways of doing what I've shown here. One thing I don't like is that the Event class is tied to the form class definition. One way to fix that is to create a new class that inherits from Form and that defines the necessary delegate.

On Windows 2000, SetForegroundWindow() doesn't always work as expected.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
I live near San Diego, CA. I wouldn't recommend it unless you can handle a severe lack of cold and snow.

Comments and Discussions

 
GeneralProcess not being killed Pin
Gonzalo Cao5-Aug-08 0:54
Gonzalo Cao5-Aug-08 0:54 
GeneralRe: Process not being killed Pin
J. David Reynolds30-Oct-08 11:44
J. David Reynolds30-Oct-08 11:44 
GeneralRe: Process not being killed Pin
drdre200530-Oct-08 12:10
drdre200530-Oct-08 12:10 
GeneralMade some slight mods for re-usability [modified] Pin
dadda_mac21-Feb-08 5:27
dadda_mac21-Feb-08 5:27 
Your code was great!! Exactly what I needed to restore my system tray application. The changes below made it a little easier for me to reuse the code, but I'm sure someone here could abstract this even further. You did a good job with this, kudos!

Note: I had to edit this, made some more abstractions - hope this helps someone.

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

namespace SingleInstanceNoMutex
{
    /// <summary>
    /// This implements single instance.  It turns out that single instance is
    /// fairly easy with either mutex or event.  The other part of the job is
    /// to get the first instance to display its window in the foreground, which
    /// is much more difficult, especially if the app window is initially hidden.
    /// this class should be created in main().  the first instance will create
    /// the event.  The second instance will open it, then signal it and exit.  The first
    /// event starts a thread which waits indefinitely for the event to signal.
    /// That thread has to call Invoke() so that the main thread can update the GUI.
    /// This class must implement IDisposable to ensure that the unmanaged Win32 handle is released
    /// 
    /// SPECIAL CREDIT:
    /// I can't take any credit for this class.  Yes, I did make some mods, but
    /// the real credit goes to drdre2005, member on CodeProject.com
    /// His original code is available on the website.  The article is entitled
    /// "Enforcing Single Instance of a Hidden-Window Application"
    /// http://www.codeproject.com/KB/threads/SingleInstance.aspx
    /// This class just abstracts the things he's done to make it reusable.
    /// </summary>
    public class SingleInstance : IDisposable
    {
        private bool disposed = false;

        #region "Imported DLLs, kernel32.dll functions"
        //#############################################
        [DllImport("kernel32.dll")]
        static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
        [DllImport("kernel32.dll")]
        static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
        [DllImport("kernel32.dll")]
        static extern bool SetEvent(IntPtr hEvent);
        [DllImport("kernel32.dll")]
        static extern IntPtr OpenEvent(UInt32 dwDesiredAccess, bool bInheritable, string lpName);
        [DllImport("kernel32.dll")]
        static extern bool CloseHandle(IntPtr hHandle);
        [DllImport("kernel32.dll")]
        static extern bool ResetEvent(IntPtr hEvent);
        //#############################################
        #endregion

        private string EVENT_NAME = string.Format("Local:{0}:{1}:{2}",
            Application.CompanyName,
            Application.ProductName,
            Application.ProductVersion);
        private const uint INFINITE = 0xFFFFFFFF;
        private const uint SYNCHRONIZE = 0x00100000;
        private const uint EVENT_MODIFY_STATE = 0x0002;

        private IntPtr eventHandle = IntPtr.Zero;		// unmanaged
        private Form form = null;

        /// <summary>
        /// Execute a form base application if another instance already running on
        /// the system activate previous one
        /// </summary>
        /// <param name="frmMain">main form</param>
        /// <returns>true if no previous instance is running</returns>
        public SingleInstance(Form mainForm)
        {
            if (InstanceAlreadyExists())
            {
                SignalInstance();
            }
            else
            {
                form = mainForm;
                Application.Run(form);
                Environment.Exit(0);
            }
        }

        /// <summary>
        /// Runs based on inherited form of System.Windows.Forms.ApplicationContext
        /// If another instance is already running on the system, activate the previous one
        /// </summary>
        /// <param name="appContext">ApplicationContext</param>
        /// <returns>true if no previous instance is running</returns>
        public SingleInstance(ApplicationContext appContext)
        {
            if (InstanceAlreadyExists())
            {
                SignalInstance();
            }
            else
            {
                form = appContext.MainForm;
                Application.Run(appContext);
                Environment.Exit(0);
            }
        }

        // Destructor
        ~SingleInstance()
        {
            Dispose(false);
        }

        // after creation, call this to determine if we are the first instance
        private bool InstanceAlreadyExists()
        {
            eventHandle = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, false, EVENT_NAME);
            if (eventHandle == IntPtr.Zero)
            {
                eventHandle = CreateEvent(IntPtr.Zero, true, false, EVENT_NAME);
                if (eventHandle != IntPtr.Zero)
                {
                    Thread thread = new Thread(new ThreadStart(WaitForSignal));
                    thread.Start();
                }
                return false;
            }
            else
            {
                return true;
            }
        }

        // an instance calls this when it detects that it is
        // the second instance.  Then it exits
        private void SignalInstance()
        {
            if (eventHandle != IntPtr.Zero)
                SetEvent(eventHandle);
        }

        // thread method will wait on the event, which will signal
        // if another instance tries to start
        private void WaitForSignal()
        {
            while (true)
            {
                uint result = WaitForSingleObject(eventHandle, INFINITE);

                if (result == 0)
                {

                    ResetEvent(eventHandle);
                    if (form != null) restoreInstance();
                }
                else
                {
                    // what the heck, don't risk a busy loop
                    // just let the thread die
                    break;
                }
            }
        }

        private delegate void restoreInstanceDel();
        private void restoreInstance()
        {
            if (form.InvokeRequired)
            {
                form.Invoke(new restoreInstanceDel(restoreInstance));
            }
            else
            {
                form.Show();
                form.WindowState = FormWindowState.Normal;
                form.Activate();
            }
        }

        #region IDisposable Members
        //#############################################################
        protected virtual void Dispose(bool disposeManagedResources)
        {
            if (!this.disposed)
            {
                if (disposeManagedResources)
                {
                    // dispose managed resources
                    if (form != null)
                    {
                        form.Dispose();
                        form = null;
                    }
                }
                // dispose unmanaged resources
                if (eventHandle != IntPtr.Zero)
                    CloseHandle(eventHandle);
                eventHandle = IntPtr.Zero;

                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        //#############################################################        
        #endregion
    }
}

I import this into my project (or create a dll and reference) and use it like this:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace MyWindowsApp
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new frumSearch());
            using (new SingleInstanceNoMutex.SingleInstance(new frmSearch())) { }
        }
    }
}


modified on Tuesday, March 4, 2008 4:21 PM

Generalpassing command line arguments Pin
MarioMARTIN31-Oct-07 1:52
MarioMARTIN31-Oct-07 1:52 
QuestionHow would you do this in WPF? Pin
JamesHurst22-Aug-07 11:59
JamesHurst22-Aug-07 11:59 
AnswerRe: How would you do this in WPF? Pin
drdre200522-Aug-07 12:20
drdre200522-Aug-07 12:20 
GeneralRe: How would you do this in WPF? Pin
JamesHurst22-Aug-07 17:16
JamesHurst22-Aug-07 17:16 
AnswerRe: How would you do this in WPF? Pin
NormDroid27-Sep-07 23:30
professionalNormDroid27-Sep-07 23:30 
GeneralGreat! Pin
MarioMARTIN13-Aug-07 4:10
MarioMARTIN13-Aug-07 4:10 
GeneralRe: Great! Pin
drdre200519-Aug-07 5:34
drdre200519-Aug-07 5:34 
GeneralRe: Great! - Hidden? Pin
JamesHurst22-Aug-07 17:06
JamesHurst22-Aug-07 17:06 
GeneralRe: Great! - Hidden? Pin
MarioMARTIN22-Aug-07 19:55
MarioMARTIN22-Aug-07 19:55 
GeneralRe: Great! - Hidden? Pin
NormDroid27-Sep-07 23:21
professionalNormDroid27-Sep-07 23:21 
GeneralSome Wpf alternatives Pin
FocusedWolf3-Apr-09 5:46
FocusedWolf3-Apr-09 5:46 
GeneralSimpler solution using a Mutex Pin
Joe McRay18-Nov-06 8:39
Joe McRay18-Nov-06 8:39 
GeneralRe: Simpler solution using a Mutex Pin
Steve Hansen20-Nov-06 1:32
Steve Hansen20-Nov-06 1:32 
GeneralRe: Simpler solution using a Mutex Pin
drdre200520-Nov-06 4:45
drdre200520-Nov-06 4:45 
GeneralRe: Simpler solution using a Mutex Pin
SuperDave00729-Nov-06 4:55
SuperDave00729-Nov-06 4:55 

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.