.Net結合PInvoke超簡單實現程式單一例項執行

草上爬發表於2009-01-11
程式的例項(Application Instance)我們可以簡單將其理解為程式。假設有個沒有任何關於例項判斷程式碼的可執行檔案Test.exe,我們一旦執行了它,那麼也意味著 Windows啟動了一個新程式,這時Test.exe就有了一個例項,如果我們再次雙擊Test.exe,Windows就有了兩個同名的程式,並且 Test.exe也有了兩個例項,在Windows工作管理員中我們就會看到兩個Test.exe程式。這足以證明Windows是支援多程式和多執行緒的,哈哈,這還用說!

多例項肯定要消耗不必要的記憶體,而且假設某些龐大的管理系統的一個例項也許會鎖定某些資源,譬如資料庫連線、檔案讀寫等等,當啟動另外一個例項時那麼肯定會報錯,我們不可能指望使用者來理解這種情況,因為使用者的水平參差不齊,我們必須加以應對。這就要求我們:

1、程式只執行一個例項;
2、當不慎啟動第二個例項時,無論前一個例項是何種狀態(最小化或者未前臺顯示),程式應有能力還原前一例項的主窗體(Restore)並將其前臺顯示,同時終止第二個例項的執行,以始終保持單一例項。

我們結合.Net框架和平臺呼叫來實現上述需求。Process 類在我們的示例中很關鍵,我們將用該類透過檢索程式名來獲取程式資訊並檢測其是否已經執行,除了使用Process我們還可以使用互斥物件(Mutex)以及記憶體對映檔案來達到同樣的結果,但是後者同前者比起來要複雜很多,而且記憶體對映檔案不被.Net原生支援,後者雖然複雜但程式檢測絕對準確,而透過檢索程式名來獲取程式資訊則有一定風險,因為可能存在兩個相同程式名卻完全不同的兩個程式,雖然這種可能性極小。本文先以檢索程式名為例,後續博文將會探索互斥物件和記憶體對映檔案結合使用來絕對保證程式的唯一性。

若已存在同名例項,則利用PInvoke平臺呼叫,透過呼叫Win32API還原前一例項的主窗體並前臺顯示。

我們建立一個預設的Windows Form. Application,雙擊Program.cs,更新程式碼如下:
    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

......

    static class Program
    {
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        private static extern bool IsIconic(IntPtr hWnd);

        private const int SW_RESTORE = 9;

        ///
        /// 應用程式的主入口點。
        ///

        [STAThread]
        static void Main()
        {
            string proc = Process.GetCurrentProcess().ProcessName;
            Process[] processes = Process.GetProcessesByName(proc);
            if (processes.Length > 1)
            {
                Process p = Process.GetCurrentProcess();
                int n = 0;
                if (processes[0].Id == p.Id)
                {
                    n = 1;
                }
                IntPtr hWnd = processes[n].MainWindowHandle;
                if (IsIconic(hWnd))
                {
                    ShowWindowAsync(hWnd, SW_RESTORE);
                }
                SetForegroundWindow(hWnd);
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMain());
        }
    }

只要在任何專案的Program.cs中按照上述程式碼更新,都能輕鬆實現程式的單一例項執行。也許有網友會疑問:我們怎麼沒看到終止程式的程式碼呢?請注意上述程式碼中的 return 語句!第二個例項的程式碼將會一直執行到在return語句之前,當我們獲取了已經執行的例項的主窗體控制程式碼並將其前臺實現之後,實際上現在在Windows 上執行了兩個同名例項!前一例項的主窗體顯示後,main函式就返回了。c/c++程式設計師會清楚main函式是程式的入口點,一個程式有且僅有一個 main函式,除非程式退出,main函式是決不會返回的!所以,return也就等於終止了程式,第二個例項就此執行結束。

上述程式碼在Windows XP SP3 + Visual Studio 2008 SP1下編譯除錯透過。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14325734/viewspace-536377/,如需轉載,請註明出處,否則將追究法律責任。

相關文章