#c #windows #winapi #named-pipes #systems-programming
Вопрос:
я работаю над проектом, в котором мне нужно запустить внешнюю программу (процесс), отправить сообщения в ее STDIN и прочитать ее STDOUT и STDERR.
Я не могу использовать анонимные каналы, потому что я нахожусь в многопоточной асинхронной среде, я прочитал много документации и примеров MSDN, и у меня есть кое-что, что работает.
Я создал именованный канал и создал несколько дескрипторов с одинаковым именем, каждый для разных потоков, ожидание входящих сообщений от STDOUT и STDERR (дочернего процесса) выполняется в другом потоке.
Я хотел реализовать часть «отправить сообщение», в которой я могу писать в STDIN ребенка, но я не смог заставить ее работать, по крайней мере, я так думал. Я написал небольшую программу, чтобы видеть, что происходит, и использовал ее в качестве своего дочернего процесса.
программа предоставляет вам три варианта: 1 запись в STDOUT, 2 запись в STDERR и 3 выход.
У меня есть вопросы/проблемы :
1 — когда я использую std::cin для чтения того, что поступает от моей родительской/серверной программы, я не могу получить значение, которое отправляю, но при использовании fread для чтения байта из STDIN это работает, и я могу получить свое значение.
2 — я создал каналы с FILE_FLAG_OVERLAPPED, должен ли я использовать перекрывающуюся структуру для своей операции записи (я не мог заставить ее работать), потому что я просто использовал функцию записи и поставил НУЛЬ в перекрывающейся структуре.
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
ZeroMemory(amp;si, sizeof(si));
si.cb = sizeof(si);
if (fHideConsole)
{
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
}
ZeroMemory(amp;pi, sizeof(pi));
// Sets the bInheritHandle flag so that pipes are inherited (TODO depends on what is open)
SECURITY_DESCRIPTOR saDesc;
InitializeSecurityDescriptor(amp;saDesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(amp;saDesc, TRUE, NULL, FALSE);
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = amp;saDesc;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
si.hStdInput = INVALID_HANDLE_VALUE;
if (inReadCallback)
{
//if (!CreateOverlappedPipe(amp;fOutPipe, amp;outPipeWrite, amp;sa))
// err = GetLastError();
//if (!SetHandleInformation(fOutPipe, HANDLE_FLAG_INHERIT, 0))
// err = GetLastError();
fOutPipe = CreateNamedPipe(
L"\\.\Pipe\RemoteExeOut",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, // Number of pipes
kSizeRead, // Out buffer size
kSizeRead, // In buffer size
0, // Timeout in ms
amp;sa);
si.hStdOutput = CreateFileW(
L"\\.\Pipe\RemoteExeOut",
FILE_WRITE_DATA,
0, // No sharing
amp;sa,
OPEN_EXISTING,
0,
NULL);
}
if (inReadErrorCallback)
{
fErrPipe = CreateNamedPipe(
L"\\.\Pipe\RemoteExeErr",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, // Number of pipes
kSizeRead, // Out buffer size
kSizeRead, // In buffer size
0, // Timeout in ms
amp;sa);
si.hStdError = CreateFileW(
L"\\.\Pipe\RemoteExeErr",
FILE_WRITE_DATA,
0, // No sharing
amp;sa,
OPEN_EXISTING,
0,
NULL);
}
fInPipe = CreateNamedPipe(
L"\\.\Pipe\RemoteExeIn",
PIPE_ACCESS_OUTBOUND, //PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, // Number of pipes
kSizeRead, // Out buffer size
kSizeRead, // In buffer size
0, // Timeout in ms
amp;sa
);
si.hStdInput = CreateFileW(
L"\\.\Pipe\RemoteExeIn",
FILE_READ_DATA,
0, // No sharing
amp;sa,
OPEN_EXISTING,
0,
NULL);
if (CreateProcessW(nullptr, cmd, nullptr, nullptr, TRUE, 0, inEnvironment, currDir, amp;si, amp;pi))
{
// custom code to send to thread
fIsRunning = true;
fPid = pi.dwProcessId;
WaitData* wData = new WaitData{ pi.dwProcessId, static_cast<DWORD>(inTimeoutSeconds), this, inTerminatedCallback };
wData->readHandle = fOutPipe;
wData->readCallback = inReadCallback;
wData->errorHandle = fErrPipe;
wData->errorCallback = inReadErrorCallback;
fWaitingTask = new VTask(NULL, 0, eTaskStylePreemptive, WaitFct);
fWaitingTask->SetKind('WLEP');
fWaitingTask->SetKindData(reinterpret_cast<sLONG_PTR>(wData));
fWaitingTask->Run();
}
else
{
vThrowWin32Error(GetLastError()); // custom error handling
}
delete[] cmd;
delete[] currDir;
// Close process and thread handles.
if (pi.hProcess != INVALID_HANDLE_VALUE)
CloseHandle(pi.hProcess);
if (pi.hThread != INVALID_HANDLE_VALUE)
CloseHandle(pi.hThread);
if (si.hStdInput != INVALID_HANDLE_VALUE)
CloseHandle(si.hStdInput);
if (si.hStdOutput != INVALID_HANDLE_VALUE)
CloseHandle(si.hStdOutput);
if (si.hStdError != INVALID_HANDLE_VALUE)
CloseHandle(si.hStdError);
моя функция записи :
DWORD dwWritten = 0;
OVERLAPPED ovr;
BOOL success = WriteFile(fInPipe, inBuffer, inSize, amp;dwWritten, nullptr);
if (!success)
{
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE)
DebugMsg(" !!!!!!!!! BROKEN PIPE !!!!!!!");
if (err == ERROR_IO_PENDING)
DebugMsg(" IO IS PENDING ASYNC USE");
}
заранее спасибо.
Комментарии:
1. Я не уверен, что вижу проблему в использовании анонимных каналов. Не могли бы вы поподробнее, пожалуйста?
2. для чего вы создаете 3 пары труб, когда достаточно 1 ? (если вы только не хотите разделять stdout и stderr ). тогда для чего вы создаете файлы для асинхронного ввода-вывода, когда вы действительно им не пользуетесь ? (std::cin, fread, поместите NULL в перекрывающуюся структуру)
3. @Someprogrammerdude вы не можете использовать анонимные каналы для асинхронного ввода-вывода.
4. @RbMm у меня большие проблемы с пониманием того, что ты пытаешься сказать
5. @BobMaza — мы можем использовать анонимные каналы для асинхронного ввода-вывода. Другой вопрос, что
ZwCreateNamedPipeFile
для этого нужно использовать. я пытаюсь спросить — для чего вы используете так много пар труб, когда одной пары достаточно. и для чего вам нужен асинхронный ввод-вывод, когда вы действительно этого не делаете