具有命名管道WinAPI的异步I / O.

时间:2010-07-12 00:10:08

标签: c++ winapi asynchronous named-pipes

好的,我已经问了一些关于尝试完成我想要做的事情的不同方面的问题。这次我只是从命名管道中读取大问题。我想我已经收集了足够的信息来完成我正在进行的项目,如果我可以正确设置它。我将在下面包含所有相关代码,但我的任务是这样:从我执行编写的程序中读取输出(连续)并将其发布到WinAPI。所以我的问题是我刚刚从匿名管道切换到命名管道,我在尝试正确设置它们时遇到问题,因此我可以检索信息。我有一个基于MSDN示例的框架设置。

#define WAIT_TIME 2 // 2s
#define INSTANCES 4 // Number of threads
#define CONNECT_STATE 0
#define READ_STATE 1
#define WRITE_STATE 2
#define WORLDRD 0
#define WORLDWR 1
#define WORLDINRD 2
#define WORLDINWR 3
#define BUFSIZE 0x1000 // Buffer size 4096 (in bytes)
#define PIPE_TIMEOUT 0x1388 // Timeout 5000 (in ms)

void Arc_Redirect::createProcesses()
{
TCHAR programName[]=TEXT("EXEC_PROGRAM.exe");
PROCESS_INFORMATION pi; 
STARTUPINFO si;
BOOL bSuccess = FALSE; 

ZeroMemory(hEvents,(sizeof(hEvents)*INSTANCES));
ZeroMemory(outStd,(sizeof(PIPE_HANDLES)*INSTANCES));

// Prep pipes
for(int i=0;i<INSTANCES;i++)
{
    hEvents[i] = ::CreateEvent(NULL, TRUE, FALSE, NULL);

    if(hEvents[i] == NULL)
        throw "Could not init program!";

    outStd[i].o1.hEvent = hEvents[i];

    outStd[i].hPipeInst = ::CreateNamedPipe(
        TEXT("\\\\.\\pipe\\arcworld"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES, BUFSIZE*sizeof(TCHAR), BUFSIZE*sizeof(TCHAR), PIPE_TIMEOUT, NULL);

    if(outStd[i].hPipeInst == INVALID_HANDLE_VALUE)
        throw "Could not init program!";

    outStd[i].pendingIO = getState(outStd[i].hPipeInst,&outStd[i].o1);

    outStd[i].dwState = outStd[i].pendingIO ?
        CONNECT_STATE : READ_STATE;
}

// Set stuff up
ZeroMemory( &pi, sizeof(PROCESS_INFORMATION));
ZeroMemory( &si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO); 
si.hStdError = outStd[WORLDRD].hPipeInst;
si.hStdOutput = outStd[WORLDRD].hPipeInst;
si.hStdInput = outStd[WORLDINWR].hPipeInst;
si.dwFlags |= STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES|FILE_FLAG_OVERLAPPED;

    // Start our process with the si info
CreateProcess(programName,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
}

BOOL Arc_Redirect::getState(HANDLE hPipe, LPOVERLAPPED lpo)
{
    BOOL connected, pendingIO = FALSE;

    // Overlap connection for this pipe
    connected = ::ConnectNamedPipe(hPipe,lpo);

    if(connected)
        throw "ConnectNamedPipe(); failed!";

    switch(GetLastError())
    {
        case ERROR_IO_PENDING:
            pendingIO = TRUE;
        break;
        case ERROR_PIPE_CONNECTED:
            if(SetEvent(lpo->hEvent))
                break;  
        default:
            throw "ConnectNamedPipe(); failed!";
        break;
    }

    return pendingIO;
}

outStd [INSTANCES]定义为PIPE_HANDLES(自定义结构),位于

之下
typedef struct
{
    HANDLE hPipeInst;
    OVERLAPPED o1;
    TCHAR chReq[BUFSIZE];
    TCHAR chReply[BUFSIZE];
    DWORD dwRead;
    DWORD dwWritten;
    DWORD dwState;
    DWORD cbRet;
    BOOL pendingIO;
} PIPE_HANDLES, *LPSTDPIPE;

现在从这里开始我有点失落。我不知道该往哪里去。我尝试在MSDN示例中使用循环,但它无法正常运行我想要做的事情。我需要读取管道的读取端并检索信息(再次,连续),同时打开一个写入端,以便我可能需要写入它。有没有人有任何想法?我一直在尝试使用匿名管道来执行ReadFile(),但它似乎没有以相同的方式工作。

另外,请注意:代码有点草率因为我一直在使用它,所以我道歉。在我使其正常运行后,我一定会清理它。

3 个答案:

答案 0 :(得分:2)

你应该有两个OVERLAPPED结构,一个用于阅读,一个用于写作。当你想要关闭管道时,每个管道还需要一个事件句柄,当你想要中止所有事件(并关闭应用程序)时,还需要一个事件句柄。对于当前所有管道都涉及的每个操作,您可以有一个WaitForMultipleObjects,或者在两个线程中分别读取写入,每个线程中包含一个WFMO。我只使用一个线程,因为关闭管道然后更简单(否则你需要在管道句柄上有一些引用计数,并且只有当引用计数降到零时才关闭它。)

当你得到一个事件然后处理它,并在刚处理完一个事件之后的数组中的所有句柄上尝试WFMO 0秒。这样就不会有管道挨饿。当0秒WFMO过去时重复正常的WFMO。

如果您需要高并发性,则在单独的线程中处理事件,并省略WFMO中当前处理的句柄。但是,跟踪所有句柄会变得有点复杂。

答案 1 :(得分:1)

您是否尝试在PIPE_NOWAIT来电中传递PIPE_WAIT而不是CreateNamedPipe?这将允许ReadFileWriteFile无阻塞。

或者,您是否尝试过使用异步IO?你正在传递FILE_FLAG_OVERLAPPED标志,所以这应该有效。如果你已经尝试过,你遇到了什么问题?

答案 2 :(得分:0)

在linux世界中,一个程序可以通过write / fwrite调用写入命名管道,另一个程序可以通过read / fread()读取它。

必须在读/写操作中使用命名管道的FULL路径。