Processes, Symlinks, & Windows 10
When starting external executables from C++ code, if we start them using SHELLEXECUTEINFO
on Windows 10, we cannot maintain a process handle across symlinks like we can on Windows 8.
Starting the process using:
SHELLEXECUTEINFO execInfo; execInfo.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NO_CONSOLE; //set lpVerb, lpFile, lpParameters, lpDirectory as needed if(!ShellExecuteEx(&execInfo)) { ShowError(); } //use execInfo.hProcess
We should have a value for execInfo.hProcess
for the executable the symlink points to as we do in Windows 8. On Windows 10, the executable will start but the process link is not established. Therefore, it will not be possible to read data back from or wait for the completion of the called executable.
We can get a process handle if we use mklink /H
to create a hardlink to the executable instead of a symlink, or use other functions (such as CreateProcess
) to start the executable.
Create Process Example:
/** \brief Executes a given system command \details For more info: http://www.codeproject.com/Tips/333559/CreateProcess-and-wait-for-result and http://stackoverflow.com/questions/13996502/how-to-save-executed-output-cmd-line-in-file-in-c Alternatives to CreateProcess: - system: for shell scripts, doesn't like exe's, unicode, const input: system("dir"); - WinExec - ShellExecute \param command Command to run \param wait Wait for completion of the command if true \param file \return Command ran successfully? */ bool ExecuteCommand(std::wstring command, bool wait, HANDLE* file) { verbose("Running: %S", command.c_str()); LPWSTR c = (LPWSTR)safe_copy(command.c_str()); bool successful = true; PROCESS_INFORMATION procInfo = {0}; STARTUPINFO startupInfo = {0}; startupInfo.cb = sizeof(startupInfo); startupInfo.dwFlags |= STARTF_USESTDHANDLES; startupInfo.hStdInput = NULL; startupInfo.hStdOutput = *file; startupInfo.hStdError = *file; if(!CreateProcessW(NULL, c, NULL, NULL, TRUE, NULL, NULL, NULL, &startupInfo, &procInfo)) { errmsg("Command Failed: %S", command.c_str()); successful = false; /* Write Error Message: LPSTR pszAPI = "CreateProcess"; LPVOID lpvMessageBuffer; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpvMessageBuffer, 0, NULL); std::string ptrMessage = fmt("ERROR: API = %d.\n error code = %d.\n message = %d.\n", pszAPI, GetLastError(), (LPSTR)lpvMessageBuffer); errmsg("%s", ptrMessage.c_str()); LocalFree(lpvMessageBuffer);*/ } else if(wait) { WaitForSingleObject(procInfo.hProcess, INFINITE); DWORD exitCode; int r = GetExitCodeProcess(procInfo.hProcess, &exitCode); CloseHandle(procInfo.hProcess); CloseHandle(procInfo.hThread); if(!r) { //no return code successful = false; } } free(c); return successful; }