/** This app allows to select multiple files and launch one instance of command process from windows explorer's context menu. Example of usage: Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\SystemFileAssociations\.txt\Shell\p4merge] "MultiSelectModel"="Player" [HKEY_CLASSES_ROOT\SystemFileAssociations\.txt\Shell\p4merge\Command] @="\"d:\\singleinstance.exe\" %1 \"C:\\Program Files\\Perforce\\p4merge.exe\" $files --si-timeout 400" */ #include #include #include class CLimitSingleInstance { protected: DWORD m_dwLastError; HANDLE m_hMutex; public: CLimitSingleInstance(TCHAR *strMutexName) { m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early m_dwLastError = GetLastError(); //save for use later... } ~CLimitSingleInstance(){ if (m_hMutex) //Do not forget to close handles. { CloseHandle(m_hMutex); //Do as late as possible. m_hMutex = NULL; //Good habit to be in. } } BOOL IsAnotherInstanceRunning(){ return (ERROR_ALREADY_EXISTS == m_dwLastError); } }; int timeout = 400; // ms enum { TIMER_ID = 1 }; LPTSTR *szArgList; int argCount; CLimitSingleInstance singleInstance(TEXT("Global\\{C30D92DD-DEE6-41A1-8907-B42FBE58C8A6}")); std::vector files; HINSTANCE hInst; // current instance TCHAR szWindowClass[256] = _T("SingleInstance_WindowClass"); // the main window class name // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { szArgList = CommandLineToArgvW(GetCommandLine(), &argCount); for (int i = 0; i < argCount; i++) { if (!lstrcmp(szArgList[i], _T("--si-timeout")) && i + 1 < argCount ) { timeout = std::stoi(szArgList[i + 1]); } } if (singleInstance.IsAnotherInstanceRunning()) { for (;;) { DWORD startTime = GetTickCount(); HWND wnd = FindWindow(szWindowClass, NULL); if (wnd) { LPCTSTR lpszString = szArgList[1]; COPYDATASTRUCT cds; cds.dwData = 1; // can be anything cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1); cds.lpData = (void*)lpszString; SendMessage(wnd, WM_COPYDATA, (WPARAM)wnd, (LPARAM)(LPVOID)&cds); break; } else { Sleep(50); if (GetTickCount() - startTime > timeout ) { break; // failure } } } LocalFree(szArgList); return 0; } if (szArgList ) { if (argCount > 3) { files.push_back(szArgList[1]); } else { MessageBox(0, L"Usage: singleinstance.exe \"%1\" $files [arguments]\r\n\r\n" L"Optional arguments for singleinstance (not passed to command):\r\n" L"--si-timeout