c++ - 在 C++ 中向管道发送命令

标签 c++ winapi console pandora

(如果你愿意,可以做Z snap,它会减轻心情)

在我决定投入的这个新项目中,我远远超出了我的舒适区,至少是其中的一部分。

整个项目将是一个 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);
}

最佳答案

我不太了解你的设置,但关于这个:

...and also where it accepts user input. The entire process locks at that point, I'm guessing because the ReadFromPipe() command thinks there is more to read as that line keeps getting refreshed.

很有可能。如果没有任何东西可以从管道中读取,那么您的进程就会阻塞,即卡在 ReadFile() 调用中。如果您只想在有内容可读时阅读,则需要异步 I/O 或通知机制。我不太了解 Windows,但似乎可以使用 IO 完成端口 (IOCP) 和异步回调函数来解决这个问题。也许这些链接有帮助:

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

IOCP and ReadFileEx usage

关于c++ - 在 C++ 中向管道发送命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17562913/

相关文章:

c++ - 递归求树的高度——分配给递归调用的值将返回什么值?

c++ - 有没有支持任意位置位操作的高性能C/C++库?

C++迭代扩展容器

c++ - 如何在 C++ 中为 LTEXT 控件调用 WM_CTLCOLORSTATIC 消息

string - 获取 .trim/-trim、.replace/-replace、.split/-split 和其他字符串运算符的帮助

c++ - 错误 : too few template-parameter-lists

c++ - 在 WinNT.h 中,为什么 STANDARD_RIGHTS_READ、...WRITE 和...EXECUTE 定义为相同?

c++ - static ofstream 将创建一个文件但从不写入它?

linux - 清除 Linux 虚拟控制台终端的回滚缓冲区

javascript - 在 Javascript 控制台中控制 Chrome Web Inspector?