Click here to Skip to main content
Click here to Skip to main content

Enforcing Single Instance of a Hidden-Window Application

, 17 Nov 2006
Rate this:
Please Sign up or sign in to vote.
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:

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:

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)

About the Author

drdre2005

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 PinmemberMember 39740095-Aug-08 0:54 
GeneralRe: Process not being killed PinmemberJ. David Reynolds30-Oct-08 11:44 
You're right about the thread. I added a signal in Dispose() and a "disposing" flag that is checked in the handler to see whether to exit the thread.
 
But to the original author...THANK YOU! This is the only one of the many things I've tried that actually works for restoring the app from the system tray. Nice technique.
GeneralRe: Process not being killed Pinmemberdrdre200530-Oct-08 12:10 
GeneralMade some slight mods for re-usability [modified] Pinmemberdadda_mac21-Feb-08 5:27 
Generalpassing command line arguments PinmemberMarioMARTIN31-Oct-07 1:52 
QuestionHow would you do this in WPF? Pinmemberjwhurst22-Aug-07 11:59 
AnswerRe: How would you do this in WPF? Pinmemberdrdre200522-Aug-07 12:20 
GeneralRe: How would you do this in WPF? Pinmemberjwhurst22-Aug-07 17:16 
AnswerRe: How would you do this in WPF? Pinmembernorm .net27-Sep-07 23:30 
GeneralGreat! PinmemberSmurfcoder13-Aug-07 4:10 
GeneralRe: Great! Pinmemberdrdre200519-Aug-07 5:34 
GeneralRe: Great! - Hidden? Pinmemberjwhurst22-Aug-07 17:06 
GeneralRe: Great! - Hidden? PinmemberMarioMARTIN22-Aug-07 19:55 
GeneralRe: Great! - Hidden? Pinmembernorm .net27-Sep-07 23:21 
GeneralSome Wpf alternatives PinmemberFocusedWolf3-Apr-09 5:46 
GeneralSimpler solution using a Mutex PinmemberJoe McRay18-Nov-06 8:39 
GeneralRe: Simpler solution using a Mutex PinmemberSteve Hansen20-Nov-06 1:32 
GeneralRe: Simpler solution using a Mutex Pinmemberdrdre200520-Nov-06 4:45 
GeneralRe: Simpler solution using a Mutex PinmemberSuperDave00729-Nov-06 4:55 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140709.1 | Last Updated 17 Nov 2006
Article Copyright 2006 by drdre2005
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid