Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Allowing a Windows app to only have one instance and bring up a minimized or hidden window

4.42/5 (14 votes)
13 Dec 2005CPOL2 min read 3   922  
This article shows how to keep an app to one instance and how to bring up the existing app if it is minimized or hidden, like with a notify icon in the tray.

Sample Image - WindowsAppSingleInstance.gif

Introduction

First of all, I have to say, I have debated about writing this article for a long time. There are already several articles out there that pretty much do the same thing. So why did I do it? Well, it seems that other solutions can find an existing instance of the app, but I haven’t seen too many examples that will bring up the existing instance if it is minimized or hidden.

Background

A few years back, when I was doing more Delphi programming than .NET, I had a nice little unit that helped me ensure I only had one instance of an application running on a user’s local machine. So I decided to try and do something similar in .NET. The only thing that is interesting with my solution is that it will bring up the form if it is minimized or hidden (like in a tray icon).

The Solution

First, I will tell you that I don’t know of any pure .NET solution for this. You have to do API calls to use Windows messaging. I used a Mutex to see if the app is already running. Then, I used Windows messaging to tell the app that is already running to show itself and go to the normal window state if it is minimized.

The Code

If you have never seen how to import a DLL and thus do a Windows API call, here is what the code looks like so we can send a Windows message:

C#
//C#
[DllImport("USER32.DLL", EntryPoint="BroadcastSystemMessageA",
 SetLastError=true, CharSet=CharSet.Unicode, ExactSpelling=true,
 CallingConvention=CallingConvention.StdCall)]

public static extern int BroadcastSystemMessage(Int32 dwFlags, ref Int32 
pdwRecipients, int uiMessage, int wParam, int lParam);
VB
' VB.net
<DLLIMPORT("USER32.DLL", EntryPoint:="BroadcastSystemMessageA", _
       SetLastError:=True, CharSet:=CharSet.Unicode, _
       ExactSpelling:=True, _
       CallingConvention:=CallingConvention.StdCall> _
    
Public Shared Function BroadcastSystemMessage(ByVal dwFlags As Int32, _
 ByRef pdwRecipients As Int32, ByVal uiMessage As Integer, _
 ByVal wParam As Integer, ByVal lParam As Integer) As Integer
' Leave function empty - DLLImport attribute forwards calls to 
' BroadcastSystemMessage to
' BroadcastSystemMessage in USER32.DLL.
End Function

Next, we have the method that is called in the OnLoad event of the form.

C#
//C#
private void CheckPrevious()
{
  //Check for previous instance of this app
  m_uniqueIdentifier = Application.ExecutablePath.Replace(@"\", "_");
  m_Mutex = new System.Threading.Mutex(false, m_uniqueIdentifier);

  //First register the windows message
  MessageId = RegisterWindowMessage(m_uniqueIdentifier);
  if (m_Mutex.WaitOne(1, true))
  {
   //we are the first instance don't need to do anything
  }
  else
  {
   //Cause the current form to show
   //Now brodcast a message to cause the first instance to show up
   Int32 BSMRecipients = BSM_APPLICATIONS; //Only go to applications

   Int32 tmpuint32 = 0;
   tmpuint32 = tmpuint32 | BSF_IGNORECURRENTTASK; //Ignore current app
   tmpuint32 = tmpuint32 | BSF_POSTMESSAGE; //Post the windows message
   int ret = BroadcastSystemMessage(tmpuint32, ref BSMRecipients, 
              MessageId, 0, 0);

   //A differnt instance already exists exit now.
   Application.Exit();
  } //else
}
VB
' VB.net
Private Sub checkprevious()
  'Check for previous instance of this app
  m_uniqueIdentifier = Application.ExecutablePath.Replace("\", "_")
  m_Mutex = New System.Threading.Mutex(False, m_uniqueIdentifier)
  'First register the windows message
  MessageId = RegisterWindowMessage(m_uniqueIdentifier)
  If m_Mutex.WaitOne(1, True) Then
    'we are the first instance don't need to do anything

  Else
    'Cause the current form to show
    'Now brodcast a message to cause the first instance to show up
    Dim BSMRecipients As Int32 = BSM_APPLICATIONS 'Only go to applications

    Dim tmpuint32 As Int32 = 0
    tmpuint32 = tmpuint32 Or BSF_IGNORECURRENTTASK 'Ignore current app
    tmpuint32 = tmpuint32 Or BSF_POSTMESSAGE 'Post the windows message
    Dim ret As Integer
    ret = BroadcastSystemMessage(tmpuint32, BSMRecipients, MessageId, 0, 0)
    'A differnt instance already exists exit now.
    Application.Exit()
  End If
End Sub

Finally, we have the method that checks the Windows messages for the form. Note: you must be very careful when you override this method. All Windows messages to this app go through this method.

C#
//C#
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
  //This overrides the windows messaging processing
  if (m.Msg == MessageId) //If we found our message then activate
  {
   // Set the WindowState to normal if the form is minimized.
   if (this.WindowState == FormWindowState.Minimized) 
   {
    this.Show();
    this.WindowState = FormWindowState.Normal;
   }

  // Activate the form.
  this.Activate();
  this.Focus();
  }
  else //Let the normal windows messaging process it.
  {
   base.DefWndProc(ref m);
  }

}
VB
' VB.net
Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
  'This overrides the windows messaging processing
  If m.Msg = MessageId Then 'If we found our message then activate
    ' Set the WindowState to normal if the form is minimized.
    If (Me.WindowState = FormWindowState.Minimized) Then
      Me.Show()
      Me.WindowState = FormWindowState.Normal
    End If

    ' Activate the form.
    Me.Activate()
    Me.Focus()

  Else 'Let the normal windows messaging process it.
    MyBase.DefWndProc(m)
  End If

End Sub

Conclusion

So it is a pretty straightforward solution. I hope someone finds this helpful. I saw some postings on The Code Project where people were asking about a solution for this problem, so I decided to write this article after all.

License

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