Перенаправление ввода-вывода & NTVDM

Тема в разделе "WASM.WIN32", создана пользователем Atlantic, 16 авг 2006.

  1. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    Искал по форуму и в гугле - не нашел. Суть проблемы такова: пытаюсь перенаправить ввод-вывод консольного приложения в файл. С консольным win32-приложением все получается, а с аналогичным досовским - нет. При запуске test2.bat (тот, который запускает досовскую прогу с перенаправленным вводом-выводом, см. прикрепление) запускается NTVDM, а наследованных хэндлов в нем не оказывается, и он выбрасывает эксепшн C0000008 (Invalid handle). В чем проблема, не пойму. Пробовал пример из MSDN (с пайпами) - то же самое. cmd.exe ведь как-то это делает, если запустить a2 < input.txt > output.txt
     
  2. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    Посмотрел, как cmd.exe это делает - он не перенаправляет ввод-вывод через флаг STARTF_USESTDHANDLES в StartupInfo для нового процесса. Вместо этого он, вызвав SetStdHandle, перенаправляет ввод-вывод СВОЕЙ консоли в файлы, а потом запускает процесс, который эту консоль наследует (то есть без флага CREATE_NEW_CONSOLE). Мне это не подходит - нужно, чтобы у нового процесса была своя консоль с перенаправленным вводом-выводом. Опять же, непонятно, почему в NTVDM не оказывается унаследованных хэндлов, хотя они там должны быть.
     
  3. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Странный баг, но я с таким уже один раз встречался. Видимо, в досовской проге что-то "нестандартно" реализовано. Или в NTVDM баг.
     
  4. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    А что в ней нестандартного? Обычная прога (a2.pas) на Паскале, откомпиленная в TP 7. И вряд ли в NTVDM баг, так как эти хэндлы там должны быть сразу после вызова CreateProcess и перед ResumeThread, то есть тогда, когда еще ни одна инструкция внутри NTVDM не выполнена. С win32 консолью они там в этот момент есть - поставил бряк на ResumeThread в своей проге и посмотрел в Process Explorer'е.
     
  5. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Atlantic
    Хорошо, какое ещё может быть обьяснение?
     
  6. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    Так как при запуске досовской проги на самом деле запускается NTVDM, то при его запуске система может "забыть" скопировать наследованные хэндлы. Еще хуже дела могут обстоять, если NTVDM уже запущен - тогда система использует его и не будет запускать новый.

    А может, система специально это не делает, полагая, что досовская прога все равно не сможет воспользоваться этими хэндлами.
     
  7. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    Помучался я довольно много, но решение все-таки нашел:

    test.dpr:
    Код (Text):
    1. uses Windows;
    2.  
    3. var si : _STARTUPINFOA;
    4. pi : _PROCESS_INFORMATION;
    5. hIn, hOut, h : THandle;
    6.  
    7. begin
    8.   if ParamCount = 0 then exit;
    9.   hIn := CreateFile('input.txt', GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
    10.   hOut := CreateFile('output.txt', GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
    11.   si.cb := SizeOf(si);
    12.   si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
    13.   si.wShowWindow := SW_HIDE;
    14.   si.hStdInput := 4;
    15.   si.hStdOutput := 8;
    16.   si.hStdError := 8;
    17.   CreateProcess(PAnsiChar(ParamStr(1)), nil, nil, nil, false, CREATE_SUSPENDED, nil, nil, si, pi);
    18.   DuplicateHandle(GetCurrentProcess, hIn, pi.hProcess, @h, 0, False, DUPLICATE_SAME_ACCESS);
    19.   DuplicateHandle(GetCurrentProcess, hOut, pi.hProcess, @h, 0, False, DUPLICATE_SAME_ACCESS);
    20.   ResumeThread(pi.hThread);
    21.   WaitForSingleObject(pi.hProcess, INFINITE);
    22.   CloseHandle(hIn);
    23.   CloseHandle(hOut);
    24. end.
    В этом коде хэндлы уже не наследуются, а используется тот факт, что значения хэндлов стандартного ввода-вывода, задаваемые в StartupInfo - это значения хэндлов в контексте запускаемого процесса. Потом, сразу после запуска процесса, hIn и hOut дублируются в новый процесс (при этом DuplicateHandle выбирает первое свободное значение для хэндла в новом процессе, так как там вообще еще нет хэндлов - проверено). В новом процессе появятся хэндлы со значениями 4 и 8 - как раз теми, что были указаны. И все работает. Единственное, насчет чего я не до конца уверен - что всегда будут эти самые "магические" 4 и 8.