65.9K
CodeProject is changing. Read more.
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (12 votes)

Dec 13, 2005

CPOL

2 min read

viewsIcon

107052

downloadIcon

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#
[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.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#
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.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#
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.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.