Windows: передача дескриптора файла дочернему процессу как osfhandleC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Windows: передача дескриптора файла дочернему процессу как osfhandle

Сообщение Anonymous »

Я пытаюсь реализовать взаимодействие между главным процессом, написанным на C++, и дочерним процессом, который является экземпляром Node.js в Windows. У меня нет контроля над IPC API дочернего процесса, я должен соответствовать тому, что делает Node.js.
Node.js при использовании в качестве сервера создает объект именованного канала Windows и передает это дочернему процессу как файловый дескриптор 3: Я мог бы подтвердить, создав множество процессов, что этот fd (также передается в среде как NODE_CHANNEL_FD) всегда равны 3 в дочернем процессе.
Для следующей программы Node.js:

Код: Выделить всё

const { spawn } = require('child_process');
const child = spawn('child.exe', [], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
env: env
});

child.on('message', (message) => {
let m = JSON.stringify(message);
console.log(`message from child: ${m}`)
});
child.send({foo: 123, bar: "456"});
Затем из C++ child.exe:

Код: Выделить всё

int main(int argc, char **argv)
{
HANDLE h = _get_osfhandle(3);
...
WriteFile(h, etc...);
...
ReadFile(h, etc...);
}
дает ожидаемые результаты, приложения могут взаимодействовать.
Теперь я пытаюсь сделать обратное: с хоста C++ создать и передать правильные структуры данных, чтобы гарантировать, что правильная вещь окажется как fd 3 в таблице fd дочернего процесса.
К сожалению, я не могу повторить это:
В родительском процессе , я создаю свою трубку следующим образом:

Код: Выделить всё

static constexpr auto PIPENAME = "\\\\?\\pipe\\echo.sock";

OVERLAPPED overlapped = {0};
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof sa;
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;

HANDLE serverPipeHandle = CreateNamedPipeA(PIPENAME,

PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
| WRITE_DAC
| FILE_FLAG_FIRST_PIPE_INSTANCE,

PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES,
65536,
65536,
0,
&sa);

if (serverPipeHandle == INVALID_HANDLE_VALUE) {
printf("CreateNamedPipe failed, GLE=%lu.\n", GetLastError());
return 1;
}

HANDLE client_pipe;
client_pipe = CreateFileA(PIPENAME,
GENERIC_READ | GENERIC_WRITE | WRITE_DAC,
0,
&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);

if (!ConnectNamedPipe(serverPipeHandle, NULL)) {
if (GetLastError() != ERROR_PIPE_CONNECTED) {
printf("ConnectNamedPipe failed, GLE=%lu.\n", GetLastError());
}
}
Я могу распечатать дескрипторы файлов в стиле Unix:

Код: Выделить всё

auto h = _open_osfhandle((intptr_t) serverPipeHandle, 0);

// Prints: Handle=276 (or sometimes 260 or close numbers)
fprintf(stderr, "Handle=%llu \n", (std::uintptr_t) serverPipeHandle);
// Always prints: fd=3
fprintf(stderr, "fd=%d \n", h);
Затем я создаю свой процесс:

Код: Выделить всё

STARTUPINFOA si = {0};
ZeroMemory(&si, sizeof(STARTUPINFOA));
si.cb = sizeof(si);

PROCESS_INFORMATION pi = {0};
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

CreateProcess(NULL, app, NULL, NULL,
TRUE, // handle inheritance
CREATE_SUSPENDED, NULL, NULL,
&si, &pi);

ResumeThread(pi.hThread);
Но сейчас в дочернем процессе fd 3 недоступен.
Чего мне не хватает, чтобы сделать его доступным?
Я попробовал:
  • Явно пометить дескрипторы как наследуемые перед созданием процесса:

Код: Выделить всё

SetHandleInformation(serverPipeHandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
SetHandleInformation(client_pipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
  • Вызов DuplicationHandle после CreateProcess и перед ResumeThread:

Код: Выделить всё

HANDLE hFileDup;
DuplicateHandle(GetCurrentProcess(),
serverPipeHandle,
pi.hProcess,
&hFileDup,
0,
TRUE,
DUPLICATE_SAME_ACCESS);
  • Вызов CreateProcessWithExplicitHandles, описанный в https://devblogs.microsoft.com/oldnewth ... 00/?p=8873 с массивом дескрипторов, содержащим как дескрипторы CreateNamedPipeA, так и CreateFileA.
  • Пытаюсь открыть каждый fd в дочернем процессе, чтобы узнать, пойдет ли этот fd куда-нибудь еще:

Код: Выделить всё

for (int i = -1000; i < 1000;  i++) {
auto hFile = (HANDLE) _get_osfhandle(i);
if (hFile != INVALID_HANDLE_VALUE) {
fprintf(stderr, "Trying %d ...", i);
fprintf(stderr, "success! \n");
}
}
Это печатает:

Код: Выделить всё

Trying 0 ...success!
Trying 1 ...success!
Trying 2 ...success!
То есть только stdin, stdout, stderr, но не какой-либо другой мой дескриптор. (интересно, что эти числа, похоже, не соответствуют значениям STD_OUTPUT_HANDLE, STD_OUTPUT_HANDLE и т. д.: https://learn.microsoft.com/en-us/windo ... tstdhandle).
  • Передача необработанного значения HANDLE моему дочернему процессу (например, 260 вместо 3). Это работает, но, к сожалению, не соответствует API node.js.
Таким образом, я не понимаю, каковы мои варианты, чтобы убедиться, что мой serverPipeHandle правильно в моем дочернем процессе оказывается fd=3?

Подробнее здесь: https://stackoverflow.com/questions/793 ... -osfhandle
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»