Click here to Skip to main content
15,879,096 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 78.9K   803   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 
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 
Yes, as Steve H. said -- using a Mutex on its own doesn't supply a way for the second instance to signal the first instance, which is, as I said, the hard part. Any global OS object that can be created (even a file) can be used to detect the second instance.

Your code is interesting, I'm new to C# and have not seen anything like that. Does the compiler complain if the object casted to IDisposable does not implement the IDisposable interface? I'm guessing yes. But, why would you explicitly call dispose on a .NET managed object? The other puzzling thing to me is why it is Disposed() in the else, but not actually released until after the end of using?

Thanks for your comment!
Brad Dre
drdre2005

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.