Асинхронный ввод-вывод с именованными каналами WinAPI

Хорошо, я задал несколько вопросов о различных аспектах попыток достичь того, что я хочу сделать. На этот раз у меня большие проблемы с чтением из именованного канала. Я думаю, что собрал достаточно информации, чтобы, возможно, завершить проект, над которым я работаю, если я смогу правильно его настроить. Я приведу весь соответствующий код ниже, но моя миссия такова: читать вывод (непрерывно) из программы, которую я не писал, и публиковать его в 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() так же, как и с анонимным каналом, но, похоже, он не работает таким же образом.

Также обратите внимание: код немного небрежный, потому что я работал с ним, поэтому приношу свои извинения. Я обязательно буду чистить его после того, как я добьюсь, чтобы это работало должным образом.


person RageD    schedule 12.07.2010    source источник
comment
В чем именно проблема с ReadFile? Именованные каналы и анонимные каналы работают одинаково, когда они открыты.   -  person Anthony Williams    schedule 16.07.2010
comment
Я вернулся к анонимным каналам для своих целей, однако, похоже, это та же проблема с обоими. Чтобы отправить какие-либо данные, нужно дождаться окончания передачи данных, поэтому я собираюсь продолжить работу над этим.   -  person RageD    schedule 16.07.2010


Ответы (3)


У вас должно быть две структуры OVERLAPPED, одна для чтения и одна для записи. Также вам нужен один дескриптор события для каждого канала, когда вы хотите закрыть канал, и еще одно событие, когда вы хотите прервать все (и закрыть приложение). Вы можете иметь один объект WaitForMultipleObject для каждой операции, в которой в данный момент задействованы все каналы, или разделить чтение и запись в два потока с одним WFMO в каждом. Я бы использовал только один поток, потому что тогда закрытие канала проще (иначе вам нужно иметь некоторый подсчет ссылок на дескрипторе канала и закрывать его только тогда, когда счетчик ссылок падает до нуля).

Когда вы получите одно событие, обработайте его и попробуйте WFMO с 0 секундами для всех дескрипторов, которые были в массиве после того, который вы только что обработали. Таким образом, ни одна труба не будет голодать. По истечении 0 секунд WFMO повторите обычный WFMO с самого начала.

Если вам нужен высокий параллелизм, обрабатывайте события в отдельных потоках и опускайте дескрипторы текущей обработки из WFMO. Однако отслеживание всех дескрипторов становится немного сложным.

person Dialecticus    schedule 02.08.2010

Пробовали ли вы передавать PIPE_NOWAIT вместо PIPE_WAIT в вызове CreateNamedPipe? Это позволит ReadFile и WriteFile быть неблокирующими.

В качестве альтернативы вы пытались использовать асинхронный ввод-вывод? Вы передаете флаг FILE_FLAG_OVERLAPPED, так что это должно работать. Если пробовали, с какими проблемами столкнулись?

person Anthony Williams    schedule 16.07.2010

В мире Linux одна программа может писать в именованный канал с помощью вызовов write/fwrite, а другая программа может читать его с помощью read/fread().

ПОЛНЫЙ путь именованного канала должен использоваться в операциях чтения/записи.

person karlphillip    schedule 15.07.2010