65.9K
CodeProject 正在变化。 阅读更多。
Home

允许 Windows 应用程序只有一个实例, 并最小化或隐藏窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (12投票s)

2005年12月13日

CPOL

2分钟阅读

viewsIcon

107819

downloadIcon

922

本文展示了如何保持应用程序只有一个实例,以及如何在应用程序被最小化或隐藏时将其弹出,例如通过托盘中的通知图标。

Sample Image - WindowsAppSingleInstance.gif

引言

首先,我必须说,我犹豫了很长时间才决定写这篇文章。已经有很多文章在做类似的事情了。那么我为什么要写呢?嗯,似乎其他解决方案可以找到应用程序的现有实例,但我没有看到很多示例能够在应用程序被最小化或隐藏时将其弹出。

背景

几年前,当我比现在更频繁地使用 Delphi 编程时,我有一个不错的单元,帮助我确保在用户的本地机器上只运行一个应用程序实例。所以我决定尝试在 .NET 中做一些类似的事情。我的解决方案中唯一有趣的地方是,如果表单被最小化或隐藏(例如在托盘图标中),它会将其弹出。

解决方案

首先,我要告诉你,我不知道任何纯 .NET 解决方案。你必须调用 API 才能使用 Windows 消息传递。我使用 Mutex 来查看应用程序是否已经在运行。然后,我使用 Windows 消息传递来告诉已经运行的应用程序显示自身,并在最小化时恢复到正常窗口状态。

代码

如果您从未见过如何导入 DLL 从而进行 Windows API 调用,以下是代码的样子,以便我们可以发送 Windows 消息

//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

接下来,我们有在表单的 OnLoad 事件中调用的方法。

//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

最后,我们有检查表单 Windows 消息的方法。注意:您在重写此方法时必须非常小心。所有发送到此应用程序的 Windows 消息都将通过此方法传递。

//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

结论

所以这是一个相当直接的解决方案。我希望对某人有所帮助。我在 Code Project 上看到了一些帖子,人们正在询问这个问题的解决方案,所以我决定还是写这篇文章。

© . All rights reserved.