Click here to Skip to main content
15,889,863 members
Articles / Programming Languages / Visual Basic
Article

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

Rate me:
Please Sign up or sign in to vote.
4.42/5 (14 votes)
13 Dec 2005CPOL2 min read 104.7K   913   58   34
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)


Written By
Software Developer (Senior)
United States United States
I started my programmer career over 26 years ago doing COBOL and SAS on a MVS mainframe. It didn't take long for me to move into windows programming. I started my windows programming in Delphi (Pascal) with a Microsoft SQL server back end. I started working with vb.net when the beta 2 came out in 2001. After spending most of my programming life as a windows programmer I started to check out asp.net in 2004. I achieved my MCSD.net in April 2005. I have done a lot of MS SQL database stuff. I have a lot of experience with Window Service and Web services as well. I spent three years as a consultant programing in C#. I really enjoyed it and found the switch between vb.net and C# to be mostly syntax. In my current position I am programming in C# working on WPF and MSSql database stuff. Lately I have been using VS2019.

On a personal note I am a born again Christian, if anyone has any questions about what it means to have a right relationship with God or if you have questions about who Jesus Christ is, send me an e-mail. ben.kubicek[at]netzero[dot]com You need to replace the [at] with @ and [dot] with . for the email to work. My relationship with God gives purpose and meaning to my life.

Comments and Discussions

 
QuestionSmall bug in this solution Pin
molson3404-Dec-06 8:16
molson3404-Dec-06 8:16 
AnswerRe: Small bug in this solution Pin
kubben4-Dec-06 8:53
kubben4-Dec-06 8:53 
QuestionCould this be used for external applications? Pin
Nadene19-May-06 5:03
Nadene19-May-06 5:03 
AnswerRe: Could this be used for external applications? Pin
kubben19-May-06 5:11
kubben19-May-06 5:11 
GeneralRe: Could this be used for external applications? Pin
Nadene21-May-06 22:35
Nadene21-May-06 22:35 
GeneralRe: Could this be used for external applications? Pin
kubben22-May-06 4:21
kubben22-May-06 4:21 
GeneralExactly what I needed [modified] Pin
mntc18-May-06 15:09
mntc18-May-06 15:09 
GeneralRe: Exactly what I needed Pin
kubben19-May-06 2:02
kubben19-May-06 2:02 
QuestionCounting the number of instances running? Pin
Sir Hiss18-Jan-06 4:45
Sir Hiss18-Jan-06 4:45 
AnswerRe: Counting the number of instances running? Pin
kubben18-Jan-06 7:57
kubben18-Jan-06 7:57 
GeneralOne Question Pin
HakunaMatada20-Dec-05 17:57
HakunaMatada20-Dec-05 17:57 
GeneralRe: One Question Pin
kubben21-Dec-05 2:19
kubben21-Dec-05 2:19 
GeneralRe: One Question Pin
HakunaMatada21-Dec-05 3:25
HakunaMatada21-Dec-05 3:25 
GeneralRe: One Question Pin
kubben21-Dec-05 3:30
kubben21-Dec-05 3:30 
GeneralRe: One Question Pin
Kent_Super28-Nov-06 0:25
Kent_Super28-Nov-06 0:25 
GeneralThanks Pin
Mohm'ed Melhem19-Dec-05 22:15
professionalMohm'ed Melhem19-Dec-05 22:15 
GeneralRe: Thanks Pin
kubben20-Dec-05 2:49
kubben20-Dec-05 2:49 
GeneralOther implemention of the idea Pin
Bill Seddon19-Dec-05 21:45
Bill Seddon19-Dec-05 21:45 
GeneralRe: Other implemention of the idea Pin
kubben20-Dec-05 2:53
kubben20-Dec-05 2:53 
GeneralJust a matter of checking a checkbox in VS2005 Pin
Alberto Venditti19-Dec-05 21:31
Alberto Venditti19-Dec-05 21:31 
GeneralRe: Just a matter of checking a checkbox in VS2005 Pin
Ashaman20-Dec-05 1:44
Ashaman20-Dec-05 1:44 
GeneralRe: Just a matter of checking a checkbox in VS2005 Pin
b_p_smith20-Dec-05 2:23
b_p_smith20-Dec-05 2:23 
GeneralRe: Just a matter of checking a checkbox in VS2005 Pin
kubben20-Dec-05 2:56
kubben20-Dec-05 2:56 
GeneralRe: Just a matter of checking a checkbox in VS2005 Pin
Alberto Venditti20-Dec-05 4:42
Alberto Venditti20-Dec-05 4:42 
GeneralRe: Just a matter of checking a checkbox in VS2005 Pin
kubben20-Dec-05 2:55
kubben20-Dec-05 2: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.