1

(如果你愿意,可以按 Z 键,它会减轻情绪)

在我决定潜入的这个新项目中,我离我的舒适区很远,至少是其中的一部分。

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

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

我还尝试将初始的 WriteToPipe(void) 覆盖到 WriteToPipe(char *cmd) 以允许我从外部线程调用它。(听TeamSpeak 3服务器聊天以获取特定命令的那个。)

现在我的代码是一团糟,但我清理了一下,希望有人能帮助我理解。

真的,这只是我决定在离开学校时尝试的一个暑期项目,也是我第一次创建 DLL 的经验。

Windows 钢琴吧

下面的大部分代码取自Create 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);
}
4

1 回答 1

1

我不太了解您的设置,但关于此:

...以及它接受用户输入的地方。整个过程在这一点上锁定,我猜是因为 ReadFromPipe() 命令认为随着该行不断刷新,还有更多内容需要阅读。

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

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

IOCP 和 ReadFileEx 的使用

于 2013-07-10T07:13:43.303 回答