如何确保在c ++中同时调用CreateProcess时只创build一个进程?

从这里引用:

BOOL WINAPI CreateProcess( __in_opt LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation ); 

我有两个独立的程序创build完全相同的过程,我怎么能确保如果其中一个已经创build了该过程,另一个不会创build两次?

最简单的方法是在程序启动后创建一个命名对象。 例如CreateEvent , CreateMutex等等。 要验证应用程序的存在,您可以在创建对象之前使用OpenEvent , OpenMutex等。 您可以选择(如果需要)带有“Global”前缀的对象的名称(请参阅http://msdn.microsoft.com/zh-cn/library/aa382954.aspx ),以允许所有终端只有一个进程服务器会话。

更新 :因为我可以看到有关我的建议有不同的意见,我试图更准确地解释它,并添加相应的测试示例。

主要思想是开始创建任何命名对象的应用程序是同名的对象还不存在。 这只能在内核对象命名空间中保留这个名字。 不需要实际使用该对象。 与在磁盘上创建文件相比,这种方式的优点是命名对象是临时的并由应用程序拥有 。 因此,如果应用程序结束,被杀死或以任何其他方式被终止(由于例如非常规的异常), 命名对象将被操作系统自动删除。 在下面的例子中我根本不使用CloseHandle 。 如何测试应用程序可以成功地确定它是否作为第一个实例运行。

 #include <windows.h> //#include <Sddl.h> LPCTSTR g_pszEventName = TEXT("MyTestEvent"); // TEXT("Global\\MyTestEvent") void DisplayFirstInstanceStartedMessage() { TCHAR szText[1024]; wsprintf (szText, TEXT("The first instance are started.\nThe event with the name \"%s\" is created."), g_pszEventName); MessageBox (NULL, szText, TEXT("CreateEventTest"), MB_OK); } void DisplayAlreadyRunningMessage () { TCHAR szText[1024]; wsprintf (szText, TEXT("The first instance of the aplication is already running.\nThe event with the name \"%s\" already exist."), g_pszEventName); MessageBox (NULL, szText, TEXT("CreateEventTest"), MB_ICONWARNING | MB_OK); } void DisplayErrorMessage (DWORD dwErrorCode) { if (dwErrorCode == ERROR_ALREADY_EXISTS) DisplayAlreadyRunningMessage(); else { LPTSTR pErrorString; if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | // Always search in system message table !!! FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | 0, NULL, // source of message definition dwErrorCode, // message ID // 0, // language ID // GetUserDefaultLangID(), // language ID // GetSystemDefaultLangID(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pErrorString, // pointer for buffer to allocate 0, // min number of chars to allocate NULL)) { MessageBox (NULL, pErrorString, TEXT("CreateEventTest"), MB_OK); LocalFree (pErrorString); } else { TCHAR szText[1024]; wsprintf (szText, TEXT("Error %d in the CreateEvent(..., \"%s\")"), dwErrorCode, g_pszEventName); MessageBox (NULL, szText, TEXT("CreateEventTest"), MB_OK); } } } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { //SECURITY_ATTRIBUTES sa; //BOOL bSuccess; HANDLE hEvent = OpenEvent (EVENT_MODIFY_STATE, FALSE, g_pszEventName);// EVENT_ALL_ACCESS if (hEvent == NULL) { DWORD dwErrorCode = GetLastError(); if (dwErrorCode != ERROR_FILE_NOT_FOUND) { DisplayErrorMessage(dwErrorCode); return 1; } } else { DisplayAlreadyRunningMessage(); return 0; } //sa.bInheritHandle = FALSE; //sa.nLength = sizeof(SECURITY_ATTRIBUTES); //bSuccess = ConvertStringSecurityDescriptorToSecurityDescriptor ( // TEXT("D:(A;OICI;GA;;;WD)"), // Allow full control // SDDL_REVISION_1, // &sa.lpSecurityDescriptor, // NULL); hEvent = CreateEvent (NULL, // &sa TRUE, FALSE, g_pszEventName); //sa.lpSecurityDescriptor = LocalFree (sa.lpSecurityDescriptor); if (hEvent == NULL) { DWORD dwErrorCode = GetLastError(); DisplayErrorMessage(dwErrorCode); return 1; } else DisplayFirstInstanceStartedMessage(); return 0; UNREFERENCED_PARAMETER (hInstance); UNREFERENCED_PARAMETER (hPrevInstance); UNREFERENCED_PARAMETER (lpCmdLine); UNREFERENCED_PARAMETER (nShowCmd); } 

如果想要支持来自同一台桌面或不同桌面的不同用户只能启动该程序的一个实例,则可以取消注释该已注释代码的某些部分或将该事件的名称MyTestEvent替换为Global\MyTestEvent

我希望通过这个例子,我的立场将会清楚。 在这种事件用法中,不需要WaitForSingleObject()调用。

你不能通过让你开始创建一个命名对象的过程来做到这一点。 这是一个固有的竞争条件,这个过程需要时间才能开始。 这两个程序都需要先调用CreateMutex,然后再尝试使用一致同意的名字创建第三个进程。 然后他们需要以零等待时间调用WaitForSingleObject()来获取互斥量。 无论谁得到它应该是一个应该调用CreateProcess()。

之后需要更多的工作来处理这个第三个进程的终止。

你可以使用这个功能

BOOL WINAPI EnumProcesses(__out DWORD * pProcessIds,__in DWORD cb,__out DWORD * pBytesReturned);

获取所有当前正在运行的进程的所有pid的列表,并检查进程是否正在运行?