#define UNICODE #include #include #include #define countof(array) (sizeof(array) / sizeof((array)[0])) static int child(void) { char line[1024]; int num_lines = 0; while (fgets(line, sizeof(line), stdin)) { ++num_lines; printf("(CHILD, line #%d) %s", num_lines, line); } return num_lines; } static int parent(void) { TCHAR self[MAX_PATH]; GetModuleFileName(NULL, self, countof(self)); TCHAR cmdline[MAX_PATH + 16]; wsprintf(cmdline, TEXT("\"%s\" -child"), self); // Тут можно было бы просто указать security attributes в CreatePipe, чтобы // хэндлы могли наследоваться дочерним процессом, но это создаёт неприятный // эффект. Наследуемыми становятся оба конца пайпа, и дочерний процесс кроме // read-хэндла получает ещё и write-хэндл. Когда родительский процесс // заканчивает запись и закрывает write-хэндл пайпа, этот конец пайпа не // закрывается, ведь у дочернего процесса остался унаследованный хэндл. В // результате дочерний процесс не получает ERROR_BROKEN_PIPE при ReadFile() // и из-за этого ожидает ввода вечно. // // Поэтому мы создаём ненаследуемые пайпы, а потом самостоятельно создаём // наследуемый дубликат нужного конца пайпа. HANDLE pipe_read_end, pipe_write_end; if (!CreatePipe(&pipe_read_end, &pipe_write_end, NULL, 0)) { fprintf(stderr, "CreatePipe() failed: %u\n", GetLastError()); return 1; } // Преобразуем ненаследуемый pipe_read_end в наследуемый child_pipe, заодно // закрывая уже ненужный pipe_read_end. HANDLE child_pipe; if (!DuplicateHandle(GetCurrentProcess(), pipe_read_end, GetCurrentProcess(), &child_pipe, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) { fprintf(stderr, "DuplicateHandle() failed: %u\n", GetLastError()); return 2; } STARTUPINFO startup_info = { .cb = sizeof(startup_info), .dwFlags = STARTF_USESTDHANDLES, .hStdInput = child_pipe, .hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE), // Делимся консолькой .hStdError = GetStdHandle(STD_ERROR_HANDLE), // с дочерним процессом. }; PROCESS_INFORMATION process_info; if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info)) { fprintf(stderr, "CreateProcess(child) failed: %u\n", GetLastError()); return 3; } // Этот нам больше не нужен, его унаследовал дочерний процесс. После этого // у нас остаётся только pipe_write_end. CloseHandle(child_pipe); CloseHandle(process_info.hThread); printf("Input anything, terminate by pressing Ctrl+Z\n"); char line[1024]; while (fgets(line, sizeof(line), stdin)) { if (!WriteFile(pipe_write_end, line, strlen(line), &(DWORD){0}, NULL)) { DWORD err = GetLastError(); if (err != ERROR_BROKEN_PIPE && err != ERROR_NO_DATA) { fprintf(stderr, "WriteFile(pipe) failed: %u\n", err); } break; } } printf("Waiting for a child to terminate... "); fflush(stdout); // Закрываем свой конец пайпа, дочерний процесс получит ERROR_BROKEN_PIPE и // завершится. CloseHandle(pipe_write_end); WaitForSingleObject(process_info.hProcess, INFINITE); DWORD exit_code = 0; if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) { fprintf(stderr, "GetExitCodeProcess() failed: %u\n", GetLastError()); } printf("exit code %u (should be the number of lines sent)\n", exit_code); CloseHandle(process_info.hProcess); return 0; } int main(int argc, char *argv[]) { if (argc == 2 && strcmp(argv[1], "-child") == 0) { return child(); } else { return parent(); } }