2016-06-20 6 views
2

Я пытаюсь выполнить команды powershell на C++ и получить его вывод через каналы.Перенаправление ввода и вывода Powershell.exe в каналы в C++

Моя программа отлично работает для cmd.exe. Однако, когда я пытаюсь сделать то же самое с powershell.exe, я получаю только «W» в качестве вывода.

я прокомментировал строку в коде ниже, который должен быть изменен, чтобы выполнить PowerShell.exe Ниже мой код, который работает на cmd.exe:

 HANDLE stdinRd, stdinWr, stdoutRd, stdoutWr; 
     DWORD readFromCmd(); 
     DWORD writeToCmd(CString command); 
     int main(int argc,char* argv[]) 
     { 
      SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true}; 
      if(!CreatePipe(&stdinRd, &stdinWr, &sa, 1000000) || !CreatePipe(&stdoutRd,&stdoutWr, &sa, 1000000)) 
      { 
       printf("CreatePipe()"); 
      } 
      STARTUPINFO si; 
      PROCESS_INFORMATION pi; 
      GetStartupInfo(&si); 
      si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; 
      si.wShowWindow = SW_HIDE; 
      si.hStdOutput = stdoutWr; 
      si.hStdError = stdoutWr;     
      si.hStdInput = stdinRd; 

    // If powershell.exe is invoked, it does not work, however works for cmd.exe  
      //if(!CreateProcess(TEXT("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"), NULL, NULL, NULL, TRUE,0, NULL, TEXT("C:\\Windows"), &si, &pi)) 
      if(!CreateProcess(TEXT("C:\\Windows\\System32\\cmd.exe"), NULL, NULL, NULL, TRUE,0, NULL, TEXT("C:\\Windows"), &si, &pi)) 
      { 
       printf("CreateProcess()"); 
       printf("CreateProcess() failed in initiatecmd(CString,int) method",0); 
       return -1; 
      } 

      writeToCmd(L"dir"); 
      Sleep(1000); 
      readFromCmd(); 
      getchar(); 
      TerminateProcess(pi.hProcess,0); 
      CloseHandle(pi.hProcess); 
      return 0; 

     } 
     DWORD writeToCmd(CString command) 
     { 
      DWORD ret; 
      DWORD numberofbyteswritten; 
      command.AppendChar('\n'); 

      LPSTR command_ANSI; 
      int size_needed = WideCharToMultiByte(CP_UTF8,0,command.GetString(),-1,NULL,0,NULL,NULL); 
      command_ANSI = (LPSTR) calloc(1, (size_needed + 1)* sizeof(char)); 
      WideCharToMultiByte(CP_UTF8,0,command.GetString(),-1,command_ANSI,size_needed,NULL,NULL); 

      ret = WriteFile(stdinWr, command_ANSI, size_needed-1, &numberofbyteswritten, NULL); 
      if(ret==0) 
      { 
       printf("WriteFile()"); 
       printf("WriteFile() method failed in writeToCmd(CString) method",0); 
       return 0; 
      } 

      CStringA temp; 
      temp.Format("%d",numberofbyteswritten); 
      temp += " bytes (Command:"; 
      temp+=command; 
      temp+=") are successfully written to cmd"; 
      printf("%s",temp); 
      return 1; 
     } 

     DWORD readFromCmd() 
     { 
      CString output_jsonstring; 
      DWORD ret; 
      DWORD dwRead; 

      while(1) 
      { 
       DWORD totalbytesavailable; 

       if(PeekNamedPipe(stdoutRd, NULL, 0, NULL, &totalbytesavailable, 0) == 0) 
       { 
        printf("PeekNamedPipe()"); 
        printf("PeekNamedPipe() method failed in responseHandler() method",0); 
        return 0; 
       } 
       if(totalbytesavailable != 0) 
       { 
        char output_cmd[1000000]; 
        if(ReadFile(stdoutRd, output_cmd, min(1000000,totalbytesavailable), &dwRead, NULL)==0) 
        { 
         printf("ReadFile()"); 
         printf("ReadFile() method failed in responseHandler() method",0); 
         return 0; 
        } 
        int min = min(1000000,totalbytesavailable); 
        output_cmd[min]='\0'; 
        printf("\n%s",output_cmd); 
       } 
       if(totalbytesavailable == 0) 
        break; 

       Sleep(100); 
      } 
      return 1; 
     } 

Если CreateProcess() используется для powershell, он работает не так, но я получаю только W как вывод.

В чем причина этого? И Как преодолеть эту проблему?

EDIT 1: Если я отобразить output_cmd в характере петли по характеру, как output_cmd [I], где I = 0 до StrLen (output_cmd), я получаю выход, как указано ниже:

INDOWS Р OWER S ад C opyright (C) 2 0 1 4 M icrosoft C orporation. Все права защищены .

P S C: \ Ш я п о д ш с>

и приложение зависает после этого! Он не принимает никакого ввода или не дает никакого результата после этого!

+0

Я бы предположил, что разница заключается в том, что Powershell возвращает массив объектов вместо простой строки. Какой результат вы получите, используя эту команду 'dir | из-string'? –

+0

btw: * «перенаправление на трубы» * не имеет никакого смысла, поскольку каждый вход/выход каждого процесса Windows уже ** передается по каналам, то есть как работает процесс ввода-вывода - даже Linux работает именно так. Вы можете менять прикрепленные трубы, цепочки или даже объединять их, но там не будет ничего другого, кроме ввода-вывода канала. В большинстве случаев используются * неназванные трубы *, вы можете переключиться на * named pipes *, если хотите ... – specializt

+0

также 'while (1)' is * bad code * и ** будет ** вызывать сбой приложения в какой-то момент. Довольно много людей здесь, на SO, скажут вам иначе - но немало людей также являются программистами-хобби ... просто дружелюбный совет. Не используйте бесконечные циклы. Когда-либо. – specializt

ответ

0

Вы прошли строку неположенном месте:

CreateProcess(TEXT("C:\\Windows\\System32\\cmd.exe")

фактически первый параметр должен быть NULL: CreateProcess(NULL, TEXT("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe")

0

Ваша главная точка путаницы, кажется, вокруг широких символов или байтов символов. В классических строках ASCII каждый символ - один байт. Современные системы используют Unicode, а два самых популярных аромата - UTF-8 (популярный в unix) и UTF-16, который используется большинством Windows API. Windows чаще всего (всегда?) Использует мало-endian разновидность, где первый байт - младшие 8 бит, а второй - верхние 8 бит. В юникоде первые 127 кодовых точек обратно совместимы с первыми 127 символами ASCII, поэтому буква «W» в ASCII равна 0x57, а в UTF-16 - 0x57 0x00.

Вы смешиваете ReadFile с printf. ReadFile использует явную длину для буфера и прочитанных байтов, и поэтому он может с радостью передавать UTF-16 в виде двоичных данных. Однако printf исходит из старой традиции строк ASCII, которые заканчиваются байтом NUL. Итак, с точки зрения printf вы даете ему строку длиной 1, потому что второй байт равен 0x00.

Посмотрите на это question about wide characters with printf, чтобы увидеть, что вы должны делать по-другому.

По умолчанию PowerShell записывает UTF-16 на свою консоль, где, поскольку старый файл cmd.exe по-прежнему использует строки ASCII. Оказывается, PowerShell вообще не использует свой дескриптор ввода, если вы не передадите опцию -Command -. Однако с этой опцией он переключается на строки ASCII для вывода и ввода. Итак, все, что вам действительно нужно сделать, это передать эту опцию командной строки, и все должно начаться так же, как и для Cmd.exe.

Я работал над модулем perl для этого, а не с C++, но вы можете найти my source code полезным.

BTW, я встревожен другой неправильной информации на этой странице:

  • В Windows, ручки для труб, консоли обрабатывает и файл обрабатывает каждый имеет различное поведение и не являются «все трубы» , Можно сказать, что они все ручки, и что вы можете читать/писать каждому из них и использовать их для stdin/stdout/stderr программы.

  • while(1) { if (!condition) break; ... } абсолютно функционально эквивалентен while(condition) { ... } и нет никаких причин, чтобы избежать его в стороне от стиля. Если ваше состояние не удобно вписывается в однострочное выражение, вполне разумно использовать while(1).

  • Вы не должны устанавливать первый аргумент CreateProcess в NULL, потому что он недвусмысленно сообщает Windows, какую программу вы собираетесь выполнять. Если вы передадите его во втором аргументе, вам нужно убедиться, что он правильно указан, потому что путь с пробелом в нем может запускать другую программу, чем предполагалось, или даже стать ошибкой безопасности. У вас нет есть, чтобы использовать первый аргумент, но сделайте это, если сможете.