2013-07-10 41 views
0

(请在Z卡,如果你想,它会减轻情绪)发送命令来管在C++

我很远了这一新的项目我的安乐窝,我决定潜入,在至少与其中的一部分。

整个项目将是一个可以加载到TeamSpeak 3中的DLL,并允许人们(通过一小组命令)来控制Pianobar(一个Pandora命令行播放器)。

这里的答案引导我足以让Pianobar(一个控制台应用程序)https://stackoverflow.com/a/17502224/1733365启动并运行,我可以获得它的STDOUT并显示直到它显示歌曲当前时间的位置,以及它接受用户的位置输入。整个过程锁定在那一点,我猜测,因为ReadFromPipe()命令认为有更多的读取,因为该行不断刷新。

为了让我能够从外部线程调用它,我还采取了一个重写WriteToPipe(void *)的初始WriteToPipe(void)的方法。 (听特定命令的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()调用。如果只有在需要读取内容时才需要读取,则需要异步I/O或通知机制。我并不真正了解Windows,但看起来好像是IO的完成端口(IOCP)和异步回调函数。或许这些链接可以帮助:

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

IOCP and ReadFileEx usage

+0

大部分什么在有相当类似微软提供的代码示例,但是,是的,它不是100%清楚。我希望有另一个应用程序启动Pianobar控制台应用程序,并且有一些监视聊天窗口(这发生在其他地方并且有详细记录),以便将命令发送回Pianobar控制台应用程序。我想我现在可以跳过标准输出,我似乎无法正确输入以重新定向到推出的Pianobar应用程序的STDIN。 –

+0

另外,谢谢。我对这些东西的术语知识有限,这绝对阻碍了我寻找可能答案的能力。异步I/O向我展示了Boost,在我的研究中我读过的某些地方是用来处理通过管道发送东西的,但我不知道为什么。 –