2013-08-12 25 views
3

我需要確保只有一個C++應用程序正在運行。使用Win API來確定一個可執行文件的實例是否已經在運行

使用Win API我如何;

  1. 檢索關於我當前應用程序的信息? GetCurrentProcess()將給我一個處理我的應用程序,我如何檢索有關它的信息

  2. 檢索用戶的所有正在運行的進程的列表? EnumProcesses()給出了一個列表,但似乎需要一個預先分配的緩衝區,那麼如何找出當前有多少進程正在運行?

  3. 我需要我的服務器的exe文件名比較正在運行的進程,並引發一個錯誤,如果我找到一個以上的

注:我不能使用任何Boost庫,和我我對使用類似帖子中看到的mutex不感興趣。

+3

*「我對使用互斥體不感興趣」,但是爲什麼? – Nawaz

+0

解釋原因需要很長時間,但我需要非互斥解決方案。 –

+1

然後使用事件或任何*共享*對象方法。 – Nawaz

回答

7

您可以使用CreateMutex函數來創建一個系統範圍的命名互斥體來表示您的進程是否正在運行。它將返回ERROR_ALREADY_EXISTS進程是否已經運行:

(void)::CreateMutex(NULL, 
         TRUE, 
         TEXT("My_Special_Invokation_Test_Mutex")); 
switch (::GetLastError()) { 
    case ERROR_SUCCESS: 
     // Process was not running already 
     break; 
    case ERROR_ALREADY_EXISTS: 
     // Process is running already 
     break; 
    default: 
     // Error occured, not sure whether process is running already. 
     break; 
} 

現在,如果你堅持不使用互斥體,您可以使用CreateFile函數。請確保爲dwShareMode字段傳遞零以獲得排他性訪問語義CREATE_NEWdwCreationDisposition字段(以便您只在不存在文件的情況下創建該文件)和FILE_FLAG_DELETE_ON_CLOSEdwFlagsAndAttributes參數,以便一旦您的文件被刪除過程終止。事情是這樣的:

LPCTSTR lockFileName = ...; 
(void)::CreateFile(lockFileName, 
        GENERIC_READ, 
        0, 
        NULL, 
        CREATE_NEW, 
        FILE_FLAG_DELETE_ON_CLOSE, 
        NULL); 
switch (::GetLastError()) { 
    case ERROR_SUCCESS: 
     // Process was not running already 
     break; 
    case ERROR_FILE_EXISTS: 
     // Process is running already 
     break; 
    default: 
     // Error occured, not sure whether process is running already. 
     break; 
} 

見約Temporary file generation and usage best practices如何安全處理的臨時文件這篇文章。

長話短說,當然可以爲你的任務使用鎖定文件,但我認爲這很難做到這一點。

+1

+1以獲得比我的更好(和*正確*)解決方案。 – Nawaz

+0

好吧,我確實添加了我對使用互斥鎖不感興趣的評論。 –

+0

@MarcusMacWilliam:對不起,我讀了那麼多。我現在擴大了我的答案,討論了一個基於CreateFile的解決方案(但請注意,很難讓這個解決方案正確)。 –

4

更新納瓦茲的答案的版本: -

Handle mutex = CreateMutex (0, 0, "SomeUniqueName"); 

switch (GetLastError()) 
{ 
case ERROR_ALREADY_EXISTS: 
    // app already running 
    break; 

case ERROR_SUCCESS: 
    // first instance 
    break; 

default: 
    // who knows what happened! 
    break; 
} 

這確實有一個安全問題,惡意應用程序可以創建你的應用程序開始前被稱爲「SomeUniqueName」的互斥體,那麼這會阻止你的應用程序被運行。爲了解決這個問題,你可以根據某個常量系統參數(例如MAC地址)的散列來命名該互斥體。該MSDN documentation有這樣說的單個實例應用:

如果您正在使用一個名爲互斥體,以限制應用程序只能有一個實例,在此之前,惡意用戶可以創建互斥體,防止您的應用程序無法啓動。爲了防止出現這種情況,請創建一個隨機命名的互斥體並存儲名稱,以使其只能由授權用戶獲取。或者,您可以爲此使用文件。要將您的應用程序限制爲每個用戶一個實例,請在用戶的配置文件目錄中創建一個鎖定的文件。

+0

同樣,我對使用互斥體不感興趣。 –

+0

應用程序關閉時,應該用CloseHandle()關閉互斥鎖手柄嗎? –

+0

@MarcusMacWilliam:你當然不想自己關閉互斥控制柄。操作系統會在您的流程關閉時爲您執行此操作,因此當您的流程關閉時互斥鎖將被破壞 - 這正是您想要的。如果你自己調用'CloseHandle',那麼肯定有一個階段你的進程仍在運行(恰好在'CloseHandle'返回之後),但是這個進程將被可能的併發調用視爲「未運行」。 –

1

由於不需要互斥鎖,因此可以使用文件映射來代替。到CreateFilemapping文檔說:

如果對象的函數調用前已經存在,該函數返回的句柄,現有的對象(其目前的大小,而不是指定的大小),並GetLastError返回ERROR_ALREADY_EXISTS。 如果函數失敗,則返回值爲NULL。

這導致以下無互斥的實現:

Handle h = CreateFileMapping(0, 0, PAGE_READONLY, 0, 4096, name); 
bool already_running = !!h && (GetLastError() == ERROR_ALREADY_EXISTS); 

要麼調用成功和映射已經存在,那麼另一個進程已經在運行。

或者,創建新的映射,或者調用失敗。無論哪種情況,都沒有其他進程正在運行。如果調用失敗,那麼以前可能嘗試的其他進程幾乎肯定會失敗。因爲一旦呼叫成功,映射已經存在,兩個相同的呼叫可能成功一次然後失敗的唯一可能的原因是「沒有更多的句柄剩下」,並且那不是(不應該)發生。無論如何,如果這確實發生了,你在其他地方會遇到更嚴重的問題。

這個東西可能適用於您選擇的每種類型的命名內核對象(即每個類型的內核對象同時具有CreateOpen版本)。

一個文件映射對象的優點是,如果你還想做IPC(比如說,將命令行轉發給已經運行的實例,然後退出),那麼你已經有了一個可以使用的映射一個管道也會很好)。

但除此之外,我不明白這個(或任何其他解決方案)如何以任何方式優於使用互斥方法。真的,爲什麼不使用互斥鎖?這是他們的目的。

相關問題