如何使用CreateProcess()和CreatePipe()从cmd.exe读取输出

时间:2016-03-13 11:35:54

标签: c++ windows cmd

  

如何使用CreateProcess()和CreatePipe()

从cmd.exe读取输出

我一直在尝试使用指定cmd.exe的命令行创建执行/K dir的子进程。目的是使用管道将命令的输出读回到父进程。

我已经CreateProcess()工作,但涉及管道的步骤给我带来了麻烦。使用管道时,新的控制台窗口不会显示(就像之前一样),并且父进程停留在对ReadFile()的调用中。

有没有人知道我做错了什么?

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>

#define BUFFSZ 4096

HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

int wmain(int argc, wchar_t* argv[]) 
{
    int result;
    wchar_t aCmd[BUFFSZ] = TEXT("/K dir"); // CMD /?
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;

    printf("Starting...\n");

    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

    // Create one-way pipe for child process STDOUT
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
        printf("CreatePipe() error: %ld\n", GetLastError());
    }

    // Ensure read handle to pipe for STDOUT is not inherited
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("SetHandleInformation() error: %ld\n", GetLastError());
    }

    // Create one-way pipe for child process STDIN
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) {
        printf("CreatePipe() error: %ld\n", GetLastError());
    }

    // Ensure write handle to pipe for STDIN is not inherited
    if (!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("SetHandleInformation() error: %ld\n", GetLastError());
    }

    si.cb = sizeof(STARTUPINFO);
    si.hStdError = g_hChildStd_OUT_Wr;
    si.hStdOutput = g_hChildStd_OUT_Wr;
    si.hStdInput = g_hChildStd_IN_Rd;
    si.dwFlags |= STARTF_USESTDHANDLES;

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    // Pipe handles are inherited
    sa.bInheritHandle = true;

    // Creates a child process
    result = CreateProcess(
        TEXT("C:\\Windows\\System32\\cmd.exe"),     // Module
        aCmd,                                       // Command-line
        NULL,                                       // Process security attributes
        NULL,                                       // Primary thread security attributes
        true,                                       // Handles are inherited
        CREATE_NEW_CONSOLE,                         // Creation flags
        NULL,                                       // Environment (use parent)
        NULL,                                       // Current directory (use parent)
        &si,                                        // STARTUPINFO pointer
        &pi                                         // PROCESS_INFORMATION pointer
        );

    if (result) {
        printf("Child process has been created...\n");
    }
    else {
        printf("Child process could not be created\n");
    }

    bool bStatus;
    CHAR aBuf[BUFFSZ + 1];
    DWORD dwRead;
    DWORD dwWrite;
    // GetStdHandle(STD_OUTPUT_HANDLE)

    while (true) {
        bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
        if (!bStatus || dwRead == 0) {
            break;
        }
        aBuf[dwRead] = '\0';
        printf("%s\n", aBuf);
    }

        // Wait until child process exits
        WaitForSingleObject(pi.hProcess, INFINITE);

        // Close process and thread handles
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);

        printf("Stopping...\n");

        return 0;
    }

6 个答案:

答案 0 :(得分:7)

解决问题的一个微妙方法是确保关闭不需要的管道末端:

    Us                                          Child
+------------------+                        +---------------+
|                  |                        |               |
|         g_hChildStd_IN_Wr----->g_hChildStd_IN_Rd          | 
|                  |                        |               | 
|       g_hChildStd_OUT_Rd<------g_hChildStd_OUT_Wr         |
|                  |                        |               |
+------------------+                        +---------------+

您的父进程只需要每个管道的一端:

  • 子输入管道的可写末尾
  • 子输出管道的可读端

启动子进程后:务必关闭不再需要的管道末端。

result = CreateProcess(...);

//CreateProcess demands that we close these two populated handles when we're done with them. We're done with them.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

/*
   We've given the console app the writable end of the pipe during CreateProcess; we don't need it anymore.
   We do keep the handle for the *readable* end of the pipe; as we still need to read from it.
   The other reason to close the writable-end handle now is so that there's only one out-standing reference to the writeable end: held by the console app.
   When the app closes, it will close the pipe, and ReadFile will return code 109 (The pipe has been ended).
   That's how we'll know the console app is done. (no need to wait on process handles with buggy infinite waits)
*/
CloseHandle(g_hChildStd_OUT_Wr);
g_hChildStd_OUT_Wr = 0;
CloseHandle(g_hChildStd_IN_Rd);
g_hChildStd_OUT_Wr = 0;

大多数解决方案的常见问题是人们试图等待进程句柄。这有很多问题;主要的一个是,如果你等待孩子终止,孩子将永远无法终止。

如果孩子试图通过管道向您发送输出,并且您INFINITE正在等待,那么您不会清空管道的末端。管道最终变满了。当孩子尝试写入已满的管道时,WriteFile调用等待管道有一些空间。因此,子进程将永远不会终止;你已经陷入僵局。

只需从管道中读取即可获得正确的解决方案。子流程终止后,它将CloseHandle 管道的一端。下次您尝试从管道读取时,您将被告知管道已关闭(ERROR_BROKEN_PIPE)。这就是你知道这个过程是如何完成的,你没有更多东西要读;所有没有危险的MsgWaitForSingleObject,容易错误地使用,并导致你想要避免的bug。

String outputText = "";

//Read will return when the buffer is full, or if the pipe on the other end has been broken
while (ReadFile(stdOutRead, aBuf, Length(aBuf), &bytesRead, null)
   outputText = outputText + Copy(aBuf, 1, bytesRead);

//ReadFile will either tell us that the pipe has closed, or give us an error
DWORD le = GetLastError;

//And finally cleanup
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);

if (le != ERROR_BROKEN_PIPE) //"The pipe has been ended."
   RaiseLastOSError(le);

答案 1 :(得分:0)

我认为你做的一切都是正确的。但cmd.exe在启动和ReadFile块之后不打印任何数据或打印很少量的数据。如果你移动你的周期

while (true) {
    bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
    if (!bStatus || dwRead == 0) {
        break;
    }
    aBuf[dwRead] = '\0';
    printf("%s\n", aBuf);
}

进入后台线程并运行其他循环,它将读取您的输入并将其发送到cmd.exe,我认为您可以看到任何效果。 您可以使读取缓冲区更小(例如16个字节)。

答案 2 :(得分:0)

这是一个执行您正在寻找的线程的示例(取自较大的程序)。它为stdout和stderr创建管道,用于它创建的进程,然后进入循环读取这些管道直到程序完成。

DWORD WINAPI ThreadProc(LPVOID lpParameter)
   {
#define EVENT_NAME "Global\\RunnerEvt"

   HANDLE hev;
   SECURITY_ATTRIBUTES psa;
   InitSAPtr(&psa);
   DWORD waitRc;
   DWORD bytesRead;
   int manual_triggered = 1;

   hev = CreateEvent(&psa, FALSE, FALSE, EVENT_NAME);

   // Create pipes we'll read

      for(;;)
      {

      if (manual_triggered)
         {
         waitRc = WAIT_OBJECT_0;
         manual_triggered = 0;
         }
      else
         {
         waitRc = WaitForSingleObject(hev, 500);
         }

      if (waitRc == WAIT_OBJECT_0)
         {
         `logprint`f(LOG_DBG, "Received command to run process\n");

         CreateChildOutFile();

         stdOutEvt = CreateEvent(&psa, TRUE, FALSE, 0);
         stdOutOvl.hEvent = stdOutEvt;

         stdErrEvt = CreateEvent(&psa, TRUE, FALSE, 0);
         stdErrOvl.hEvent = stdErrEvt;

         gStdOutReadHand =  CreateNamedPipe(STD_OUT_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
            PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
         if (gStdOutReadHand == INVALID_HANDLE_VALUE)
            {
            log(LOG_DBG, "Error %d on create STDOUT pipe\n", GetLastError());
            }

         gStdErrReadHand =  CreateNamedPipe(STD_ERR_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
            PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
         if (gStdErrReadHand == INVALID_HANDLE_VALUE)
            {
            log(LOG_DBG, "Error %d on create STDERR pipe\n", GetLastError());
            }

         runProcess();

         log(LOG_DBG, "After runProcess, new PID is %d/%x\n", piProcInfo.dwProcessId, piProcInfo.dwProcessId);

         if (piProcInfo.dwProcessId == 0)
            {
            log(LOG_DBG, "runProcess failed, closing child STDIN/STDERR\n");
            closeChildPipes();

#define FAIL_MSG "Child process failed to start\n"
            writeChildOutFile(FAIL_MSG, strlen(FAIL_MSG) );

            CloseHandle(hChildOut);
            }
         else
            {
            log(LOG_DBG, "Child process created, setting up for redir/restart/termination\n");

            issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
            //log(LOG_DBG, "After read set on STDOUT\n");

            issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
            //log(LOG_DBG, "After read set on STDERR\n");

            HANDLE harr[4];

            for(;;)
               {
               harr[0] = hev;
               harr[1] = piProcInfo.hProcess;
               harr[2] = stdOutEvt;
               harr[3] = stdErrEvt;

               DWORD waitRc2 = WaitForMultipleObjects(4, harr, FALSE, 500);

               #if 0
               if (waitRc2 == -1)
                  {
                  log(LOG_DBG, "Wait error %d\n", GetLastError());
                  Sleep(500);
                  }

               log(LOG_DBG, "waitRc2 %d\n", waitRc2);
               #endif


               if ((waitRc2 - WAIT_OBJECT_0) == 0)
                  {
                  log(LOG_DBG, "Woke up because another trigger command was received\n");
                  #define NEW_CMD_MSG "Child process is being terminated because new trigger received\n"

                  writeChildOutFile(NEW_CMD_MSG, strlen(NEW_CMD_MSG));

                  terminateChild();
                  CloseHandle(hChildOut);
                  manual_triggered = 1;
                  break;
                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 1)
                  {
                  //log(LOG_DBG, "Woke up because child has terminated\n");
                  closeChildPipes();
                  #define NORM_MSG "Normal child process termination\n"
                  writeChildOutFile(NORM_MSG, strlen(NORM_MSG));
                  CloseHandle(hChildOut);
                  break;
                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 2)
                  {
                  //log(LOG_DBG, "Woke up because child has stdout\n");
                  if (GetOverlappedResult(gStdOutReadHand, &stdOutOvl, &bytesRead, TRUE))
                     {
                     writeChildOutFile(stdOutBuff, bytesRead);
                     ResetEvent(stdOutEvt);
                     issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
                     }

                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 3)
                  {
                  //log(LOG_DBG, "Woke up because child has stderr\n");

                  if (GetOverlappedResult(gStdErrReadHand, &stdErrOvl, &bytesRead, TRUE))
                     {
                     writeChildOutFile(stdErrBuff, bytesRead);
                     ResetEvent(stdErrEvt);
                     issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
                     }
                  }
               else
                  {
                  if (gShuttingDown)
                     {
                     log(LOG_DBG, "Woke with active child and service is terminating\n");

#define SHUTDOWN_MSG "Child process is being terminated because the service is shutting down\n"

                     writeChildOutFile(SHUTDOWN_MSG, strlen(SHUTDOWN_MSG));
                     terminateChild();
                     CloseHandle(hChildOut);
                     break;
                     }
                  }

               if (gShuttingDown)
                  {
                  break;
                  }

               }
            }
         }
      else if (gShuttingDown)
         {
         break;
         }

      CloseHandle(gStdOutReadHand);
      CloseHandle(gStdErrReadHand);

      }

   return 0;
   }

void writeChildOutFile(char *msg, int len)
   {
   DWORD bytesWritten;
   WriteFile(hChildOut, msg, len, &bytesWritten, 0);
   }


void terminateChild(void)
   {
   if (piProcInfo.dwProcessId != 0)
      {
      TerminateProcess(piProcInfo.hProcess, -1);
      CloseHandle(piProcInfo.hThread);
      CloseHandle(piProcInfo.hProcess);
      closeChildPipes();
      }
   }

void closeChildPipes(void)
   {
   CloseHandle(g_hChildStd_OUT_Wr);
   CloseHandle(g_hChildStd_ERR_Wr);
   }

void runProcess(void)
   {
   SECURITY_ATTRIBUTES saAttr;

   // Set the bInheritHandle flag so pipe handles are inherited.
   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = NULL;

   // Create a pipe for the child process's STDOUT.
   TCHAR szCmdline[]=TEXT("cmd.exe /C C:\\temp\\RunnerService.bat");
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE;

// Set up members of the PROCESS_INFORMATION structure.

   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

   g_hChildStd_OUT_Wr = CreateFile (STD_OUT_PIPE_NAME,
                FILE_WRITE_DATA,
                0,
                &saAttr,
                OPEN_EXISTING,
                0,
                NULL);

   if (g_hChildStd_OUT_Wr == INVALID_HANDLE_VALUE)
      {
      log(LOG_DBG, "Error creating child proc stdout file %d\n", GetLastError());
      }


   g_hChildStd_ERR_Wr = CreateFile (STD_ERR_PIPE_NAME,
                FILE_WRITE_DATA,
                0,
                &saAttr,
                OPEN_EXISTING,
                0,
                NULL);

   if (g_hChildStd_ERR_Wr == INVALID_HANDLE_VALUE)
      {
      log(LOG_DBG, "Error creating child proc stderr file %d\n", GetLastError());
      }


// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.

   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO);
   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
   siStartInfo.hStdError = g_hChildStd_ERR_Wr;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.

   bSuccess = CreateProcess(NULL,
      szCmdline,     // 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

   }


void CreateChildOutFile(void)
   {
   SYSTEMTIME st;
   SECURITY_ATTRIBUTES sa;
   char fName[_MAX_PATH];

   InitSAPtr(&sa);

   GetLocalTime(&st);

   sprintf(fName, "C:\\TEMP\\runsvcchild_%02d_%02d_%02d_%04d.out", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

   hChildOut = CreateFile(fName, GENERIC_WRITE, FILE_SHARE_READ, &sa,  CREATE_ALWAYS,  FILE_ATTRIBUTE_NORMAL, 0);
   }

void issueRead(HANDLE hFile, OVERLAPPED *overLapped, char *buf, DWORD *dwRead)
   {
   //log(LOG_DBG, "Start of issueRead, hfile %08x, ovl is %08x\n", hFile, overLapped);
   BOOL brc = ReadFile(hFile, buf, 4096, dwRead, overLapped);
   if (!brc)
      {
      DWORD dwle = GetLastError();
      if (dwle != ERROR_IO_PENDING)
         {
         log(LOG_DBG, "Error %d on ReadFile\n", dwle);
         }
      }
   else
      {
      // log(LOG_DBG, "Read issued\n");
      }
   }  

答案 3 :(得分:0)

我也有同样的情况。就我而言,来自Lib,需要执行内部exe并读取输出。以下工作没有任何问题。

  void executeCMDInNewProcessAndReadOutput(LPSTR lpCommandLine)
    {
        STARTUPINFO si;
        SECURITY_ATTRIBUTES sa;
        PROCESS_INFORMATION pi;
        HANDLE g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr, g_hChildStd_OUT_Rd, g_hChildStd_IN_Wr;  //pipe handles
        char buf[1024];           //i/o buffer

        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = TRUE;
        sa.lpSecurityDescriptor = NULL;

        if (CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0))   //create stdin pipe
        {
            if (CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0))  //create stdout pipe
            {

                //set startupinfo for the spawned process
                /*The dwFlags member tells CreateProcess how to make the process.
                STARTF_USESTDHANDLES: validates the hStd* members.
                STARTF_USESHOWWINDOW: validates the wShowWindow member*/
                GetStartupInfo(&si);

                si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
                si.wShowWindow = SW_HIDE;
                //set the new handles for the child process
                si.hStdOutput = g_hChildStd_OUT_Wr;
                si.hStdError = g_hChildStd_OUT_Wr;
                si.hStdInput = g_hChildStd_IN_Rd;

                //spawn the child process
                if (CreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
                    NULL, NULL, &si, &pi))
                {
                    unsigned long bread;   //bytes read
                    unsigned long avail;   //bytes available
                    memset(buf, 0, sizeof(buf));

                    for (;;)
                    {
                        PeekNamedPipe(g_hChildStd_OUT_Rd, buf, 1023, &bread, &avail, NULL);
                        //check to see if there is any data to read from stdout
                        if (bread != 0)
                        {
                            if (ReadFile(g_hChildStd_OUT_Rd, buf, 1023, &bread, NULL))
                            {
                                break;
                            }
                        }
                    }

                    //clean up all handles
                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                    CloseHandle(g_hChildStd_IN_Rd);
                    CloseHandle(g_hChildStd_OUT_Wr);
                    CloseHandle(g_hChildStd_OUT_Rd);
                    CloseHandle(g_hChildStd_IN_Wr);
                }
                else
                {
                    CloseHandle(g_hChildStd_IN_Rd);
                    CloseHandle(g_hChildStd_OUT_Wr);
                    CloseHandle(g_hChildStd_OUT_Rd);
                    CloseHandle(g_hChildStd_IN_Wr);
                }
            }
            else
            {
                CloseHandle(g_hChildStd_IN_Rd);
                CloseHandle(g_hChildStd_IN_Wr);
            }
        }
    }

答案 4 :(得分:0)

Ian Boyd's answer had this gem: Once you've launched your child process: be sure to close those ends of the pipe you no longer need.

I've produced another version of the CreatePipe + CreateProcess solution which, I hope, is more clear:

int main()
{
    BOOL ok = TRUE;
    HANDLE hStdInPipeRead = NULL;
    HANDLE hStdInPipeWrite = NULL;
    HANDLE hStdOutPipeRead = NULL;
    HANDLE hStdOutPipeWrite = NULL;

    // Create two pipes.
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
    ok = CreatePipe(&hStdInPipeRead, &hStdInPipeWrite, &sa, 0);
    if (ok == FALSE) return -1;
    ok = CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0);
    if (ok == FALSE) return -1;

    // Create the process.
    STARTUPINFO si = { };
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdError = hStdOutPipeWrite;
    si.hStdOutput = hStdOutPipeWrite;
    si.hStdInput = hStdInPipeRead;
    PROCESS_INFORMATION pi = { };
    LPCWSTR lpApplicationName = L"C:\\Windows\\System32\\cmd.exe";
    LPWSTR lpCommandLine = (LPWSTR)L"C:\\Windows\\System32\\cmd.exe /c dir";
    LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL;
    LPSECURITY_ATTRIBUTES lpThreadAttribute = NULL;
    BOOL bInheritHandles = TRUE;
    DWORD dwCreationFlags = 0;
    LPVOID lpEnvironment = NULL;
    LPCWSTR lpCurrentDirectory = NULL;
    ok = CreateProcess(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttribute,
        bInheritHandles,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        &si,
        &pi);
    if (ok == FALSE) return -1;

    // Close pipes we do not need.
    CloseHandle(hStdOutPipeWrite);
    CloseHandle(hStdInPipeRead);

    // The main loop for reading output from the DIR command.
    char buf[1024 + 1] = { };
    DWORD dwRead = 0;
    DWORD dwAvail = 0;
    ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
    while (ok == TRUE)
    {
        buf[dwRead] = '\0';
        OutputDebugStringA(buf);
        puts(buf);
        ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
    }

    // Clean up and exit.
    CloseHandle(hStdOutPipeRead);
    CloseHandle(hStdInPipeWrite);
    DWORD dwExitCode = 0;
    GetExitCodeProcess(pi.hProcess, &dwExitCode);
    return dwExitCode;
}

Some notes:

  • The pipe for StdIn is not really required:
    • This is because the DIR command doesn't require user input (but, I left it in the code, since it is a good template for running other commands)
    • everyting to do with hStdInPipeRead & hStdInPipeWrite can be omitted
    • setting si.hStdInput can be omitted
  • Replace hardcoded L"C:\\Windows\\System32\\cmd.exe" with reading the COMSPEC environment variable.
  • Replace LPWSTR with LPTSTR if we wish to compile for non-UNICODE.
  • Replace cmd.exe /k DIR with cmd.exe /c DIR since when the DIR command finishes we don't really want the cmd.exe to stick around.

答案 5 :(得分:0)

我尝试了Stephen Quan的答案,并遇到了段错误。也许有更多经验的人可能知道为什么。无论如何,这应该是他正在尝试做的一个更正确的例子:

Week    Product     Quantity    Sales
----    -------     --------    -----
1       12a         14          1400  --> Week 1 + Week 2 + Week 3 + Week 4 but row labeled Week 1
2       12a         11          1100

以这样一种方式编写代码,使其可以在DLL中用作导出函数或在应用程序的代码本身中使用。

使用有效的参数获取valuate_shell()函数的结果,并将其发送到标准MessageBoxA()以查看其实际效果。请注意,该参数将支持UTF-8;我不确定如何使用返回值(输出)来执行此操作,因为没有CreatePipeW或ReadFileW函数。

由于静态返回值,该示例也不是线程安全的,但是由于JensÅkerblom在对Stephen Quan的答案的评论中指出,CreateProcessW()的命令行参数可能会被修改。

由于不是线程安全的,因此如果不作细微调整,就不能以异步方式真正使用它,即使用非静态C ++ std :: string作为返回值,而无需先将其转换为char指针。如果您需要异步功能和线程安全,就足够了。但是您将需要自己编写多线程。

valuate_shell_helper()函数与validate_shell()函数没有什么不同,除了前者在输出字符串的末尾包含最终的回车符和换行符,而后者则没有。

最后,从要在多个源中使用的函数中退回静态内联前缀,并且不要忘记在包含的头文件中将这些函数声明为所有使用或定义这些函数的文件。

如果任何人想通过修改编辑权限来改善此答案,或者对此答案发表评论中的建议,请随时这样做。如果有人有兴趣,我还将考虑在某个时候做一个多线程示例。