如何在 C++ 中调用 ::CreateProcess 来启动 Windows 可执行文件?
题
寻找一个例子:
- 启动一个 EXE
- 等待 EXE 完成。
- 可执行文件完成时正确关闭所有句柄。
解决方案
像这样的东西:
STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
其他提示
有一个例子在 http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx
只需更换 argv[1]
与包含程序的常量或变量。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
void _tmain( int argc, TCHAR *argv[] )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if( argc != 2 )
{
printf("Usage: %s [cmdline]\n", argv[0]);
return;
}
// Start the child process.
if( !CreateProcess( NULL, // No module name (use command line)
argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
return;
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
如果您的应用程序是 Windows GUI 应用程序,那么使用下面的代码进行等待并不理想,因为您的应用程序的消息将不会得到处理。对于用户来说,您的应用程序看起来像是挂起的。
WaitForSingleObject(&processInfo.hProcess, INFINITE)
像这样的东西 未经测试的 下面的代码可能会更好,因为它将继续处理 Windows 消息队列,并且您的应用程序将保持响应:
//-- wait for the process to finish
while (true)
{
//-- see if the task has terminated
DWORD dwExitCode = WaitForSingleObject(ProcessInfo.hProcess, 0);
if ( (dwExitCode == WAIT_FAILED )
|| (dwExitCode == WAIT_OBJECT_0 )
|| (dwExitCode == WAIT_ABANDONED) )
{
DWORD dwExitCode;
//-- get the process exit code
GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
//-- the task has ended so close the handle
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
//-- save the exit code
lExitCode = dwExitCode;
return;
}
else
{
//-- see if there are any message that need to be processed
while (PeekMessage(&message.msg, 0, 0, 0, PM_NOREMOVE))
{
if (message.msg.message == WM_QUIT)
{
return;
}
//-- process the message queue
if (GetMessage(&message.msg, 0, 0, 0))
{
//-- process the message
TranslateMessage(&pMessage->msg);
DispatchMessage(&pMessage->msg);
}
}
}
}
如果您的 exe 碰巧是控制台应用程序,您可能有兴趣阅读 stdout 和 stderr - 为此,我将谦虚地向您推荐以下示例:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q190351
这是一个有点拗口的代码,但我使用了该代码的变体来生成和读取。
在半相关的说明中,如果您想启动一个比当前进程具有更多权限的进程(例如,从以普通用户身份运行的主应用程序启动需要管理员权限的管理应用程序),您不能在 Vista 上使用 CreateProcess() 来执行此操作,因为它不会触发 UAC 对话框(假设它已启用)。不过,使用 ShellExecute() 时会触发 UAC 对话框。
请记住,使用 WaitForSingleObject
在这种情况下会给你带来麻烦。以下内容摘自我网站上的提示:
出现问题的原因是您的应用程序有一个窗口但没有发送消息。如果生成的应用程序使用广播目标之一调用 SendMessage (HWND_广播 或者 HWND_TOPMOST),那么 SendMessage 不会返回到新应用程序,直到所有应用程序都处理了该消息 - 但您的应用程序无法处理该消息,因为它没有泵送消息......所以新的应用程序会锁定,所以你的等待永远不会成功......僵局。
如果您对生成的应用程序有绝对的控制权,那么您可以采取一些措施,例如使用 SendMessageTimeout 而不是 SendMessage(例如用于 DDE 启动(如果有人仍在使用它)。但有些情况会导致您无法控制的隐式 SendMessage 广播,例如使用 SetSysColors API。
解决这个问题唯一安全的方法是:
- 将等待拆分为一个单独的线程,或者
- 在等待上使用超时并在等待循环中使用 PeekMessage 以确保泵送消息,或者
- 使用
MsgWaitForMultipleObjects
API。
这是一个适用于 Windows 10 的新示例。当使用windows10 sdk时,你必须使用CreateProcessW。这个例子有注释并且希望是不言自明的。
#ifdef _WIN32
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include <cstdlib>
#include <string>
#include <algorithm>
class process
{
public:
static PROCESS_INFORMATION launchProcess(std::string app, std::string arg)
{
// Prepare handles.
STARTUPINFO si;
PROCESS_INFORMATION pi; // The function returns this
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
//Prepare CreateProcess args
std::wstring app_w(app.length(), L' '); // Make room for characters
std::copy(app.begin(), app.end(), app_w.begin()); // Copy string to wstring.
std::wstring arg_w(arg.length(), L' '); // Make room for characters
std::copy(arg.begin(), arg.end(), arg_w.begin()); // Copy string to wstring.
std::wstring input = app_w + L" " + arg_w;
wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() );
const wchar_t* app_const = app_w.c_str();
// Start the child process.
if( !CreateProcessW(
app_const, // app path
arg_concat, // Command line (needs to include app path as first argument. args seperated by whitepace)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
throw std::exception("Could not create child process");
}
else
{
std::cout << "[ ] Successfully launched child process" << std::endl;
}
// Return process handle
return pi;
}
static bool checkIfProcessIsActive(PROCESS_INFORMATION pi)
{
// Check if handle is closed
if ( pi.hProcess == NULL )
{
printf( "Process handle is closed or invalid (%d).\n", GetLastError());
return FALSE;
}
// If handle open, check if process is active
DWORD lpExitCode = 0;
if( GetExitCodeProcess(pi.hProcess, &lpExitCode) == 0)
{
printf( "Cannot return exit code (%d).\n", GetLastError() );
throw std::exception("Cannot return exit code");
}
else
{
if (lpExitCode == STILL_ACTIVE)
{
return TRUE;
}
else
{
return FALSE;
}
}
}
static bool stopProcess( PROCESS_INFORMATION &pi)
{
// Check if handle is invalid or has allready been closed
if ( pi.hProcess == NULL )
{
printf( "Process handle invalid. Possibly allready been closed (%d).\n");
return 0;
}
// Terminate Process
if( !TerminateProcess(pi.hProcess,1))
{
printf( "ExitProcess failed (%d).\n", GetLastError() );
return 0;
}
// Wait until child process exits.
if( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_FAILED)
{
printf( "Wait for exit process failed(%d).\n", GetLastError() );
return 0;
}
// Close process and thread handles.
if( !CloseHandle( pi.hProcess ))
{
printf( "Cannot close process handle(%d).\n", GetLastError() );
return 0;
}
else
{
pi.hProcess = NULL;
}
if( !CloseHandle( pi.hThread ))
{
printf( "Cannot close thread handle (%d).\n", GetLastError() );
return 0;
}
else
{
pi.hProcess = NULL;
}
return 1;
}
};//class process
#endif //win32