2013-07-10 1 views
0

(Выполните Z оснастки, если вы хотите, это будет облегчить настроение)Послать команду трубы в C++

Я очень далеко от моей зоны комфорта на этом новом проекте я решил нырнуть, в наименее с его частями.

Весь проект будет DLL, которая может быть загружена в TeamSpeak 3, и позволит людям (через небольшой набор команд) управлять Pianobar (игроком командной строки Pandora).

Ответ на этот вопрос дал мне достаточно для того, чтобы запустить Pianobar (консольное приложение) https://stackoverflow.com/a/17502224/1733365, я могу получить его STDOUT и отображать его до тех пор, пока оно не покажет текущее время песни, а также где оно принимает пользователя вход. Весь процесс блокируется в этот момент, я предполагаю, потому что команда ReadFromPipe() считает, что читать больше, поскольку эта строка продолжает обновляться.

Я также взял удар, чтобы переопределить исходный WriteToPipe (void) в WriteToPipe (char * cmd), чтобы позволить мне называть его из внешнего потока. (Тот, кто слушает чат сервера TeamSpeak 3 для определенных команд.)

Прямо сейчас мой код - гигантский беспорядок, но я немного его очистил, поэтому, надеюсь, кто-то может мне помочь.

Действительно, это всего лишь летний проект, который я решил попробовать, пока я не в школе, и мой первый опыт создания DLL.

Pianobar for Windows

Большая часть кода ниже, был взят из Creating a Child Process with Redirected Input and Output

#include "pianobar.h" 
//#include <windows.h> 
//#include <tchar.h> 
//#include <stdio.h> 
#include <strsafe.h> 
//#include <stdlib.h> 
//#include <sys/types.h> 
//#include <string.h> 

#define BUFSIZE 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; 

HANDLE g_hInputFile = NULL; 
PROCESS_INFORMATION piProcInfo; 
STARTUPINFO siStartInfo; 
SECURITY_ATTRIBUTES saAttr; 

void CreateChildProcess(void); 
void WriteToPipe(char *command); 
void ReadFromPipe(void); 
void ErrorExit(PTSTR); 

int pianobar (struct TS3Functions ts3Functions) { 
    int iFound = 0; 

    printf("\n->Start of parent execution.\n"); 

    // 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. 

    if (! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) 
     ErrorExit(TEXT("StdoutRd CreatePipe")); 

    // Ensure the read handle to the pipe for STDOUT is not inherited. 

    if (! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) 
     ErrorExit(TEXT("Stdout SetHandleInformation")); 

    // Create a pipe for the child process's STDIN. 

    if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) 
     ErrorExit(TEXT("Stdin CreatePipe")); 

    // Ensure the write handle to the pipe for STDIN is not inherited. 

    if (! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) 
     ErrorExit(TEXT("Stdin SetHandleInformation")); 

    // Create the child process. 

    CreateChildProcess(); 

    // Write to the pipe that is the standard input for a child process. 
    // Data is written to the pipe's buffers, so it is not necessary to wait 
    // until the child process is running before writing data. 

    // This should cause a help menu to be displayed on the next ReadFromPipe() 
    // However, ReadFromPipe() doesn't show help commands 
    //WriteToPipe("?\r\n"); 

    // Read from pipe that is the standard output for child process. 
    // Reading causes a lock. 
    //ReadFromPipe(); 


    printf("\n->End of parent execution.\n"); 
    printf("\n->Pianobar started.\n"); 
    iFound = 1; 
    return iFound; 
} 

void CloseChildProcess() { 
    //CloseHandle(piProcInfo.hProcess); 
    CloseHandle(piProcInfo.hThread); 
    TerminateProcess(piProcInfo.hProcess,0); 
} 

void CreateChildProcess() 
    // Create a child process that uses the previously created pipes for STDIN and STDOUT. 
{ 
    TCHAR szCmdline[]=TEXT("c:\\pianobar\\pianobar.exe"); 
    BOOL bSuccess = FALSE; 

    // Set up members of the PROCESS_INFORMATION structure. 

    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); 

    // 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.hStdError = g_hChildStd_OUT_Wr; 
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; 
    siStartInfo.hStdInput = g_hChildStd_IN_Rd; 
    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 
     TEXT("c:\\pianobar\\"),   // use parent's current directory 
     &siStartInfo, // STARTUPINFO pointer 
     &piProcInfo); // receives PROCESS_INFORMATION 

    // If an error occurs, exit the application. 
    if (! bSuccess) 
     ErrorExit(TEXT("CreateProcess")); 
    else 
    { 
     // Close handles to the child process and its primary thread. 
     // Some applications might keep these handles to monitor the status 
     // of the child process, for example. 

     // I think I need these while I'm running... 
     //CloseHandle(piProcInfo.hProcess); 
     //CloseHandle(piProcInfo.hThread); 
    } 
} 

void WriteToPipe(char *command) 

    // Read from a file and write its contents to the pipe for the child's STDIN. 
    // Stop when there is no more data. 
{ 

    DWORD dwRead, dwWritten; 
    DWORD dw; 
    CHAR chBuf[BUFSIZE]; 
    BOOL bSuccess = FALSE; 
    LPTSTR lpTStr; 

    printf("\n-> In WriteToPipe()\n"); 
    bSuccess = WriteFile(g_hChildStd_IN_Wr, command, sizeof(command), &dwWritten, NULL); 
     if(bSuccess) { 
      printf("bSuccess was TRUE\n->Sent: "); 
      printf(command); 
     } else { 
      printf("bSuccess was FALSE\n"); 
     } 

     // Close the pipe handle so the child process stops reading. 
     // my 2nd call to WriteToPipe results in a "The handle is invalid" error 
     if (! CloseHandle(g_hChildStd_IN_Wr)) { 

     dw = GetLastError(); 
     FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER | 
      FORMAT_MESSAGE_FROM_SYSTEM | 
      FORMAT_MESSAGE_IGNORE_INSERTS, 
      NULL, 
      dw, 
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
      (LPTSTR) &lpTStr, 
      0, NULL); 
      printf(lpTStr); 
     } 
     if(command == "q\r\n") { 
      printf("Quit received.\n"); 
      // this should have killed the process if it was received correctly... 
      CloseChildProcess(); 
     } 
} 

void ReadFromPipe(void) 
    // Read output from the child process's pipe for STDOUT 
    // and write to the parent process's pipe for STDOUT. 
    // Stop when there is no more data. 
{ 
    DWORD dwRead, dwWritten; 
    CHAR chBuf[BUFSIZE]; 
    BOOL bSuccess = FALSE; 
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 

    printf("\n-> In ReadFromPipe()\n"); 
    for (;;) 
    { 
     bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); 
     if(! bSuccess || dwRead == 0) break; 
     printf("In ReadFromPipe loop\n"); 
     bSuccess = WriteFile(hParentStdOut, chBuf, 
      dwRead, &dwWritten, NULL); 
     if (! bSuccess) { 
      // we never get to this, it just waits... 
      printf("Leaving loop\n"); 
      break; 
     } 
    } 
} 

void ErrorExit(PTSTR lpszFunction) 

    // Format a readable error message, display a message box, 
    // and exit from the application. 
{ 
    LPVOID lpMsgBuf; 
    LPVOID lpDisplayBuf; 
    DWORD dw = GetLastError(); 

    FormatMessage(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | 
     FORMAT_MESSAGE_FROM_SYSTEM | 
     FORMAT_MESSAGE_IGNORE_INSERTS, 
     NULL, 
     dw, 
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
     (LPTSTR) &lpMsgBuf, 
     0, NULL); 

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
     (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
     LocalSize(lpDisplayBuf)/sizeof(TCHAR), 
     TEXT("%s failed with error %d: %s"), 
     lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf); 
    LocalFree(lpDisplayBuf); 
    ExitProcess(1); 
} 

ответ

1

Я действительно не понимаю, ваши настройки, но в отношении к этому:

... а также где он принимает вход пользователя. Весь процесс блокируется в этом точках, я предполагаю, потому что команда ReadFromPipe() думает, что есть больше, чтобы читать, поскольку эта строка продолжает обновляться.

Это очень вероятно. Если нет ничего, чтобы читать из трубы, тогда блоки процесса, то есть застревают внутри вызова ReadFile(). Если вы хотите читать только в том случае, если есть что-то для чтения, вам нужен асинхронный ввод-вывод или механизм уведомления. Я действительно не знаю Windows, но похоже, что для этого доступны функции «IOP» (IOCP) и асинхронные функции обратного вызова. Может быть, эти ссылки помогут:

What is the best epoll/kqueue/select equvalient on Windows?

IOCP and ReadFileEx usage

+0

Большинство, что там очень похож на пример кода, предоставленной Microsoft, но да, это не 100% ясно. Я хотел бы, чтобы другое приложение запустило консольное приложение Pianobar и провело что-то в окне чата (это происходит в другом месте и хорошо документировано), чтобы отправлять команды обратно в консольное приложение Pianobar. Я думаю, что сейчас могу пропустить STDOUT, я, похоже, не могу войти в перенаправление на STDIN правильно для запущенного приложения Pianobar. –

+0

Также, спасибо. Мое ограниченное знание терминологии этих вещей определенно сдерживает мою способность искать возможные ответы. Async I/O показал мне Boost, который я читал где-то в своих исследованиях как полезный для работы с передачей через каналы, но я не знал почему. –