簡體   English   中英

將命令發送到C ++中的管道

[英]Send command to pipe in C++

(如果需要,可以執行Z快照,這樣可以減輕心情)

在我決定參與的這個新項目中,我遠遠超出了我的舒適范圍,至少是其中一部分。

整個項目將是一個DLL,可以將其加載到TeamSpeak 3中,並允許人們(通過一小組命令)控制Pianobar(Pandora命令行播放器)。

此處的答案足以引導我啟動並運行Pianobar(控制台應用程序) https://stackoverflow.com/a/17502224/1733365 ,我可以獲取其STDOUT並一直顯示直到顯示歌曲當前位置為止時間,以及接受用戶輸入的位置。 我猜整個過程都在這一點上鎖定,因為ReadFromPipe()命令認為隨着該行不斷刷新,還有更多要讀取的內容。

我還嘗試了將初始WriteToPipe(void)重寫為WriteToPipe(char * cmd),以便允許我從外部線程調用它。 (用於偵聽TeamSpeak 3服務器聊天的特定命令的人。)

現在,我的代碼是一團糟,但是我整理了一下,希望有人可以幫助我理解。

真的,這只是我決定在放學時嘗試的一個夏季項目,也是我第一次創建DLL的經歷。

Windows版鋼琴架

下面的許多代碼來自創建具有重定向輸入和輸出的子進程

#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);
}

我不太了解您的設置,但是關於此:

...以及接受用戶輸入的位置。 我猜整個過程都在這一點上鎖定,因為ReadFromPipe()命令認為隨着該行不斷刷新,還有更多要讀取的內容。

那很有可能。 如果沒有什么要從管道讀取的內容,則您的進程將阻塞,即被卡在ReadFile()調用中。 如果僅在有需要閱讀的內容時才想閱讀,則需要異步I / O或通知機制。 我不是很了解Windows,但似乎可以使用IO完成端口(IOCP)和異步回調函數。 這些鏈接可能有助於:

Windows上最佳的epoll / kqueue / select等效是什么?

IOCP和ReadFileEx的用法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM