【问题标题】:How do I call ::CreateProcess in c++ to launch a Windows executable?如何在 C++ 中调用 ::CreateProcess 来启动 Windows 可执行文件?
【发布时间】:2010-09-07 17:41:56
【问题描述】:

寻找一个例子:

  1. 启动一个 EXE
  2. 等待 EXE 完成。
  3. 在可执行文件完成时正确关闭所有句柄。

【问题讨论】:

    标签: c++ windows winapi


    【解决方案1】:

    类似这样的:

    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);
    }
    

    【讨论】:

    • WaitForSingleObject 行应该是 ::WaitForSingleObject(processInfo.hProcess, INFINITE)(不带 '&')
    • 为什么在 WaitForSingleObject 上使用 :: 来指定全局命名空间,而不是在其他 API 调用上?
    • 如果你不打算使用它,你应该立即关闭线程句柄。不过,这不是什么大问题。
    • 为什么要立即关闭?它不节省任何资源
    • 你缺少一个 ;在 WaitForSingleObject() 调用之后。
    【解决方案2】:

    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 );
    }
    

    【讨论】:

      【解决方案3】:

      如果您的应用程序是 Windows GUI 应用程序,则使用下面的代码进行等待并不理想,因为您的应用程序的消息将不会得到处理。在用户看来,您的应用程序已挂起。

      WaitForSingleObject(&processInfo.hProcess, INFINITE)
      

      像下面的 untested 代码可能会更好,因为它将继续处理 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);
            }
          }
        }
      }
      

      【讨论】:

      【解决方案4】:

      如果您的 exe 恰好是一个控制台应用程序,您可能有兴趣阅读 stdout 和 stderr -- 为此,我会谦虚地向您推荐这个示例:

      http://support.microsoft.com/default.aspx?scid=kb;EN-US;q190351

      这有点拗口的代码,但我使用了这段代码的变体来生成和阅读。

      【讨论】:

        【解决方案5】:

        在半相关的说明中,如果您想启动一个比您当前进程具有更多权限的进程(例如,从作为普通用户运行的主应用程序启动一个需要管理员权限的管理应用程序),您在 Vista 上使用 CreateProcess() 不能这样做,因为它不会触发 UAC 对话框(假设它已启用)。不过,使用 ShellExecute() 时会触发 UAC 对话框。

        【讨论】:

          【解决方案6】:

          这是一个适用于 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
          

          【讨论】:

          • std::copy() 是将std::string 转换为std::wstring 的错误方法。这仅适用于 ASCII 数据而不会丢失数据。对于非 ASCII 数据,您需要使用 MultiByteToWideChar() 或其他类似的函数/库转换数据。 const_cast&lt;wchar_t*&gt;(input.c_str()) 使用也是错误的。请改用 &amp;input[0],或在 C++17 中使用 input.data()
          【解决方案7】:

          【讨论】:

          • 不错。一个链接只回答 404 File not found.
          【解决方案8】:

          请记住,在这种情况下,使用 WaitForSingleObject 可能会给您带来麻烦。以下内容摘自我网站上的提示:

          出现问题是因为您的应用程序有一个窗口但没有发送消息。如果生成的应用程序使用广播目标之一(HWND_BROADCASTHWND_TOPMOST)调用 SendMessage,则在所有应用程序处理完该消息之前,SendMessage 不会返回到新应用程序- 但是您的应用无法处理消息,因为它没有发送消息....所以新应用会锁定,所以您的等待永远不会成功....死锁。

          如果您对生成的应用程序具有绝对控制权,那么您可以采取一些措施,例如使用 SendMessageTimeout 而不是 SendMessage(例如,对于 DDE 启动,如果有人仍在使用它)。但是有些情况会导致您无法控制的隐式 SendMessage 广播,例如使用 SetSysColors API。

          唯一安全的方法是:

          1. 将 Wait 拆分成一个单独的线程,或者
          2. 在等待上使用超时并在等待循环中使用 PeekMessage 以确保您泵送消息,或者
          3. 使用MsgWaitForMultipleObjects API。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-11
            • 1970-01-01
            • 1970-01-01
            • 2013-02-28
            • 1970-01-01
            相关资源
            最近更新 更多