在開(kāi)發(fā) WinForms 應(yīng)用程序時(shí),有時(shí)需要防止同一個(gè)應(yīng)用程序的多個(gè)實(shí)例同時(shí)運(yùn)行。這種需求在某些情況下非常重要,例如,當(dāng)你需要確保某個(gè)資源(如文件或數(shù)據(jù)庫(kù))只被一個(gè)應(yīng)用實(shí)例訪問(wèn)時(shí)。
本文將介紹幾種防止同一應(yīng)用運(yùn)行多個(gè)實(shí)例的方法,提供詳細(xì)的代碼示例,并輸出為 Markdown 格式。
方法一:使用 Mutex
類(lèi)
Mutex
(互斥量)是一個(gè)同步基元,它可以用于跨線程和進(jìn)程同步。通過(guò)創(chuàng)建一個(gè)命名互斥量,可以防止應(yīng)用運(yùn)行多個(gè)實(shí)例。
示例代碼
namespace SingleInstanceApp
{
internal static class Program
{
private static Mutex mutex = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
const string mutexName = "MyApp";
bool isOwned;
mutex = new Mutex(true, mutexName, out isOwned);
if (!isOwned)
{
MessageBox.Show("應(yīng)用程序已經(jīng)在運(yùn)行中。", "多實(shí)例檢測(cè)", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
GC.KeepAlive(mutex);
}
}
}
在上述代碼中,我們使用 Mutex
類(lèi)創(chuàng)建了一個(gè)系統(tǒng)全局命名的互斥體 mutexName
。如果應(yīng)用程序已經(jīng)在運(yùn)行,則 isOwned
將為 false
,應(yīng)用會(huì)顯示一條消息并退出。
方法二:使用 Process
類(lèi)
通過(guò) Process
類(lèi)檢查當(dāng)前是否已經(jīng)有同名進(jìn)程在運(yùn)行,也可以防止多個(gè)實(shí)例的運(yùn)行。
示例代碼
using System.Diagnostics;
namespace SingleInstanceApp
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
if (IsAlreadyRunning())
{
MessageBox.Show("應(yīng)用程序已經(jīng)在運(yùn)行中。", "多實(shí)例檢測(cè)", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
static bool IsAlreadyRunning()
{
string currentProcessName = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(currentProcessName);
return processes.Length > 1;
}
}
}
此方法通過(guò) Process.GetProcessesByName
方法獲取當(dāng)前運(yùn)行的同名進(jìn)程。如果長(zhǎng)度大于1,說(shuō)明此時(shí)已有另一個(gè)實(shí)例在運(yùn)行。
方法三:使用 Windows API
還有一種方法是利用 Windows API 創(chuàng)建一個(gè)命名事件,檢查該事件是否已經(jīng)存在。
示例代碼
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SingleInstanceApp
{
internal static class Program
{
const string UniqueEventName = "Global\\MyApp";
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
IntPtr handle = CreateEvent(IntPtr.Zero, false, false, UniqueEventName);
if (handle == IntPtr.Zero || GetLastError() == 183) // ERROR_ALREADY_EXISTS (183)
{
MessageBox.Show("應(yīng)用程序已經(jīng)在運(yùn)行中。", "多實(shí)例檢測(cè)", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
}
上述代碼使用了 CreateEvent
API 創(chuàng)建一個(gè)命名事件,并通過(guò) GetLastError
檢查事件是否已經(jīng)存在(錯(cuò)誤代碼 183 表示該事件已存在)。
CreateEvent
是一個(gè) Windows API 函數(shù),用于創(chuàng)建或打開(kāi)一個(gè)命名的或未命名的事件對(duì)象。事件對(duì)象在進(jìn)程間和線程間同步中非常有用。
GetLastError()
函數(shù)是用于檢索擴(kuò)展的錯(cuò)誤信息的函數(shù)。它通常與其他 Windows API 函數(shù)一起使用,這些函數(shù)不返回明確的錯(cuò)誤代碼,但是如果調(diào)用失敗,可以通過(guò) GetLastError()
獲取詳細(xì)的錯(cuò)誤信息。

總結(jié)
以上介紹了三種在 WinForms 開(kāi)發(fā)中防止同一應(yīng)用運(yùn)行多個(gè)實(shí)例的方法:
使用 Mutex
類(lèi)。
使用 Process
類(lèi)。
使用 Windows API。
每種方法都有其優(yōu)點(diǎn)和適用場(chǎng)景,開(kāi)發(fā)者可根據(jù)具體需求選擇合適的方法來(lái)實(shí)現(xiàn)多實(shí)例檢測(cè)功能。希望此文對(duì)你有所幫助,歡迎提出任何問(wèn)題或建議。
該文章在 2024/7/23 22:28:12 編輯過(guò)