How to wait for ShellExecute to run?
我已经设法在VC ++中使用ShellExecute来启动文档。
现在,我希望运行一个接收一些参数的命令行工具,并在后台运行(隐藏而不是最小化),并使其阻止我的程序流,以便我可以等待它完成。
我如何更改以下命令行:
1 | ShellExecute(NULL,"open",FULL_PATH_TO_CMD_LINE_TOOL,ARGUMENTS,NULL,SW_HIDE); |
问题是,我有一个将html转换为pdf的工具,希望该工具完成后(也就是pdf)准备好,让另一个ShellExecute对其进行查看。
有一篇CodeProject文章显示了如何使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | SHELLEXECUTEINFO ShExecInfo = {0}; ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; ShExecInfo.hwnd = NULL; ShExecInfo.lpVerb = NULL; ShExecInfo.lpFile ="c:\\\\MyProgram.exe"; ShExecInfo.lpParameters =""; ShExecInfo.lpDirectory = NULL; ShExecInfo.nShow = SW_SHOW; ShExecInfo.hInstApp = NULL; ShellExecuteEx(&ShExecInfo); WaitForSingleObject(ShExecInfo.hProcess, INFINITE); CloseHandle(ShExecInfo.hProcess); |
关键点是标志
Use to indicate that the hProcess member receives the process handle. This handle is typically used to allow an application to find out when a process created with
ShellExecuteEx terminates
另外,请注意:
The calling application is responsible for closing the handle when it is no longer needed.
您也可以使用CreateProcess代替ShellExecute / ShellExecuteEx。此函数包括一个cmd.exe包装器选项,返回退出代码,并返回stdout。 (其中的内容可能并不完美)。
注意:在我的使用中,我知道必须有stdout结果,但是PeekedNamePipe函数并不总是在第一次尝试时就返回字节数,因此会在那里循环。也许,有人可以弄清楚这一点并发布修订?另外,也许应该产生一个替代版本,分别返回stderr?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #include <stdio.h> #include <iostream> #include <fstream> #include <sstream> #include <Shellapi.h> /* Note: The exitCode for a"Cmd Process" is not the exitCode for a sub process launched from it! That can be retrieved via the errorlevel variable in the command line like so: set errorlevel=&[launch command]&echo.&echo exitCode=%errorlevel%&echo. The stdOut vector will then contain the exitCode on a seperate line */ BOOL executeCommandLine( const CStringW &command, DWORD &exitCode, const BOOL asCmdProcess=FALSE, std::vector<CStringW> *stdOutLines=NULL ) { // Init return values BOOL bSuccess = FALSE; exitCode = 0; if( stdOutLines ) stdOutLines->clear(); // Optionally prepend cmd.exe to command line to execute CStringW cmdLine( (asCmdProcess ? L"cmd.exe /C" : L"" ) + command ); // Create a pipe for the redirection of the STDOUT // of a child process. HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; bSuccess = CreatePipe( &g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0); if( !bSuccess ) return bSuccess; bSuccess = SetHandleInformation( g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0 ); if( !bSuccess ) return bSuccess; // Setup the child process to use the STDOUT redirection PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = g_hChildStd_OUT_Wr; siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Execute a synchronous child process & get exit code bSuccess = CreateProcess( NULL, cmdLine.GetBuffer(), // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo ); // receives PROCESS_INFORMATION if( !bSuccess ) return bSuccess; WaitForSingleObject( piProcInfo.hProcess, (DWORD)(-1L) ); GetExitCodeProcess( piProcInfo.hProcess, &exitCode ); CloseHandle( piProcInfo.hProcess ); CloseHandle( piProcInfo.hThread ); // Return if the caller is not requesting the stdout results if( !stdOutLines ) return TRUE; // Read the data written to the pipe DWORD bytesInPipe = 0; while( bytesInPipe==0 ){ bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesInPipe, NULL ); if( !bSuccess ) return bSuccess; } if( bytesInPipe == 0 ) return TRUE; DWORD dwRead; CHAR *pipeContents = new CHAR[ bytesInPipe ]; bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents, bytesInPipe, &dwRead, NULL); if( !bSuccess || dwRead == 0 ) return FALSE; // Split the data into lines and add them to the return vector std::stringstream stream( pipeContents ); std::string str; while( getline( stream, str ) ) stdOutLines->push_back( CStringW( str.c_str() ) ); return TRUE; } |
如果使用COM,则有时无法使用
Because ShellExecuteEx can delegate execution to Shell extensions
(data sources, context menu handlers, verb implementations) that are
activated using Component Object Model (COM), COM should be
initialized before ShellExecuteEx is called. Some Shell extensions
require the COM single-threaded apartment (STA) type. In that case,
COM should be initialized as shown here:
1 | CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) |
There are instances where ShellExecuteEx does not use one of these
types of Shell extension and those instances would not require COM to
be initialized at all. Nonetheless, it is good practice to always
initalize COM before using this function.
来自MSDN的更多信息
https://docs.microsoft.com/zh-CN/windows/win32/api/shellapi/nf-shellapi-shellexecuteexa