Искал по форуму и в гугле - не нашел. Суть проблемы такова: пытаюсь перенаправить ввод-вывод консольного приложения в файл. С консольным win32-приложением все получается, а с аналогичным досовским - нет. При запуске test2.bat (тот, который запускает досовскую прогу с перенаправленным вводом-выводом, см. прикрепление) запускается NTVDM, а наследованных хэндлов в нем не оказывается, и он выбрасывает эксепшн C0000008 (Invalid handle). В чем проблема, не пойму. Пробовал пример из MSDN (с пайпами) - то же самое. cmd.exe ведь как-то это делает, если запустить a2 < input.txt > output.txt
Посмотрел, как cmd.exe это делает - он не перенаправляет ввод-вывод через флаг STARTF_USESTDHANDLES в StartupInfo для нового процесса. Вместо этого он, вызвав SetStdHandle, перенаправляет ввод-вывод СВОЕЙ консоли в файлы, а потом запускает процесс, который эту консоль наследует (то есть без флага CREATE_NEW_CONSOLE). Мне это не подходит - нужно, чтобы у нового процесса была своя консоль с перенаправленным вводом-выводом. Опять же, непонятно, почему в NTVDM не оказывается унаследованных хэндлов, хотя они там должны быть.
Странный баг, но я с таким уже один раз встречался. Видимо, в досовской проге что-то "нестандартно" реализовано. Или в NTVDM баг.
А что в ней нестандартного? Обычная прога (a2.pas) на Паскале, откомпиленная в TP 7. И вряд ли в NTVDM баг, так как эти хэндлы там должны быть сразу после вызова CreateProcess и перед ResumeThread, то есть тогда, когда еще ни одна инструкция внутри NTVDM не выполнена. С win32 консолью они там в этот момент есть - поставил бряк на ResumeThread в своей проге и посмотрел в Process Explorer'е.
Так как при запуске досовской проги на самом деле запускается NTVDM, то при его запуске система может "забыть" скопировать наследованные хэндлы. Еще хуже дела могут обстоять, если NTVDM уже запущен - тогда система использует его и не будет запускать новый. А может, система специально это не делает, полагая, что досовская прога все равно не сможет воспользоваться этими хэндлами.
Помучался я довольно много, но решение все-таки нашел: test.dpr: Код (Text): uses Windows; var si : _STARTUPINFOA; pi : _PROCESS_INFORMATION; hIn, hOut, h : THandle; begin if ParamCount = 0 then exit; hIn := CreateFile('input.txt', GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0); hOut := CreateFile('output.txt', GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0); si.cb := SizeOf(si); si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW; si.wShowWindow := SW_HIDE; si.hStdInput := 4; si.hStdOutput := 8; si.hStdError := 8; CreateProcess(PAnsiChar(ParamStr(1)), nil, nil, nil, false, CREATE_SUSPENDED, nil, nil, si, pi); DuplicateHandle(GetCurrentProcess, hIn, pi.hProcess, @h, 0, False, DUPLICATE_SAME_ACCESS); DuplicateHandle(GetCurrentProcess, hOut, pi.hProcess, @h, 0, False, DUPLICATE_SAME_ACCESS); ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(hIn); CloseHandle(hOut); end. В этом коде хэндлы уже не наследуются, а используется тот факт, что значения хэндлов стандартного ввода-вывода, задаваемые в StartupInfo - это значения хэндлов в контексте запускаемого процесса. Потом, сразу после запуска процесса, hIn и hOut дублируются в новый процесс (при этом DuplicateHandle выбирает первое свободное значение для хэндла в новом процессе, так как там вообще еще нет хэндлов - проверено). В новом процессе появятся хэндлы со значениями 4 и 8 - как раз теми, что были указаны. И все работает. Единственное, насчет чего я не до конца уверен - что всегда будут эти самые "магические" 4 и 8.