First, you are calling WaitForSingleObject()
and CloseHandle()
if CreateProcess()
fails, which is useless. Don't call those functions unless it succeeds instead.
Second, you are calling the Unicode version of CreateProcess()
, which has a caveat that your code is not handling. Per the CreateProcess()
documentation:
lpCommandLine [in, out, optional]
The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
Third, if you want to terminate the process after a timeout, you could use TerminateProcess()
, but that is brute force and should be avoided when possible. Sublime has a UI, so the preferred solution is to ask the UI to close itself down and then wait for it to do so, as documented on MSDN:
How To Terminate an Application "Cleanly" in Win32.
If you absolutely must shut down a process, follow these steps:
Post a WM_CLOSE to all Top-Level windows owned by the process that you want to shut down. Many Windows applications respond to this message by shutting down.
NOTE: A console application's response to WM_CLOSE depends on whether or not it has installed a control handler.
Use EnumWindows() to find the handles to your target windows. In your callback function, check to see if the windows' process ID matches the process you want to shut down. You can do this by calling GetWindowThreadProcessId(). Once you have established a match, use PostMessage() or SendMessageTimeout() to post the WM_CLOSE message to the window.
Use WaitForSingleObject() to wait for the handle of the process. Make sure you wait with a timeout value, because there are many situations in which the WM_CLOSE will not shut down the application. Remember to make the timeout long enough (either with WaitForSingleObject(), or with SendMessageTimeout()) so that a user can respond to any dialog boxes that were created in response to the WM_CLOSE message.
If the return value is WAIT_OBJECT_0, then the application closed itself down cleanly. If the return value is WAIT_TIMEOUT, then you must use TerminateProcess() to shutdown the application.
NOTE: If you are getting a return value from WaitForSingleObject() other then WAIT_OBJECT_0 or WAIT_TIMEOUT, use GetLastError() to determine the cause.
By following these steps, you give the application the best possible chance to shutdown cleanly (aside from IPC or user-intervention).
With that said, try something more like this:
BOOL CALLBACK SendWMCloseMsg(HWND hwnd, LPARAM lParam)
{
DWORD dwProcessId = 0;
GetWindowThreadProcessId(hwnd, &dwProcessId);
if (dwProcessId == lParam)
SendMessageTimeout(hwnd, WM_CLOSE, 0, 0, SMTO_ABORTIFHUNG, 30000, NULL);
return TRUE;
}
...
STARTUPINFO siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
WCHAR szFilename[] = L"C:\full path to\source.cpp";
if (CreateProcess(L"C:\Program Files\Sublime Text 2\sublime_text.exe",
szFileName,
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE,
NULL,
NULL,
&siStartupInfo,
&piProcessInfo))
{
CloseHandle(piProcessInfo.hThread);
WaitForInputIdle(piProcessInfo.hProcess, INFINITE);
if (WaitForSingleObject(piProcessInfo.hProcess, SomeTimeoutHere) == WAIT_TIMEOUT)
{
EnumWindows(&SendWMCloseMsg, piProcessInfo.dwProcessId);
if (WaitForSingleObject(piProcessInfo.hProcess, AnotherTimeoutHere) == WAIT_TIMEOUT)
{
// application did not close in a timely manner, do something...
// in this example, just kill it. In a real world
// app, you should ask the user what to do...
TerminateProcess(piProcessInfo.hProcess, 0);
}
}
CloseHandle(piProcessInfo.hProcess);
}