Я установил PowerShellInside в соответствии с предложением JPBlanc, но не использовал его очень долго. Одна вещь, связанная с подключением, слишком ограничивающая, и мне не нравится, когда ее ограничивают (особенно если это ограничение основано на прибыли, но это совершенно другое обсуждение, в которое я не должен вступать). И несмотря на то, что это решение исходной проблемы, оно кажется неудовлетворительным, потому что не решает проблему программирования, с которой я столкнулся.
Однако мне в конечном итоге удалось решить указанную проблему, действительно, используя вызовы windows api в процессе обертки. Поскольку ловушек довольно много, я решил ответить на свой вопрос и дать другим людям, которые смотрят на ту же проблему, несколько указателей. Основная структура выглядит следующим образом:
- Запустите процесс оболочки с помощью перенаправленного stdin / -out (и stderr, если хотите). (В моем случае stdin и out будут потоками управляющих последовательностей и данных xterm, потому что это путь ssh)
- Использование GetStdHandle () возвращает перенаправленные маркеры ввода и вывода. Далее SetStdHandle () для CreateFile () из «CONIN $» и «CONOUT $», так что дочерние процессы наследуют консоль и не имеют перенаправлений процесса-оболочки. (Обратите внимание, что для createfile необходим дескриптор безопасности, разрешающий наследование)
- Настройка режима консоли, размера, заголовка, обработчиков Ctrl-C и т. Д. Примечание: не забудьте установить шрифт, если вам нужна поддержка юникода, я использовал Lucida Console (.FontFamily = 54, .FaceName = "Lucida Console" ). Без этого чтение символов из вывода вашей консоли вернет кодированные версии, с которыми ужасно работать в управляемом коде.
- Чтение вывода может быть выполнено с помощью SetWinEventHook (), обязательно используйте внеконтекстное уведомление, потому что я вполне уверен, что запуск вашего управляемого приложения в другом контексте процесса / адресном пространстве является плохой идеей ™ (Я так уверен, что даже не пытался). Событие будет запускаться для каждого окна консоли, а не только для вашего собственного. Поэтому отфильтруйте все вызовы обратного вызова по дескриптору окна. Получить дескриптор окна текущего консольного приложения с помощью GetConsoleWindow (). Также не забудьте отменить обратный вызов, когда приложение будет сделано.
- Обратите внимание, что до этого момента не следует использовать (или делать что-либо, что вызывает загрузку) класс System.Console, иначе скорее всего все пойдет не так. Использование после этой точки будет вести себя так, как если бы подпроцесс записывал в вывод.
- Создать необходимый подпроцесс (обратите внимание, вы должны использовать .UseShellExecute = false, иначе он не будет наследовать консоль)
- Вы можете начать ввод данных на консоль, используя WriteConsoleInput ()
- В этот момент (или в отдельном потоке) вы должны запустить цикл сообщений Windows, иначе вы не получите обратные вызовы уведомлений о событиях консоли. Для этого вы можете просто использовать Application.Run () без параметров. Чтобы разорвать цикл сообщений, вы должны в какой-то момент опубликовать сообщение о выходе из цикла сообщений. Я сделал это с помощью Application.Exit () в событии .Exited подпроцесса. (Обратите внимание, используйте .EnableRaisingEvents, чтобы это работало)
- Теперь будут выполняться вызовы вашего обратного вызова win, когда что-то на вашей консоли изменится. Обратите внимание на событие прокрутки, это может работать несколько неожиданно. Также не делайте предположений о синхронной доставке. Если подпроцесс записывает 3 строки, то к тому времени, когда вы обрабатываете первое событие, оставшиеся 3 строки могут быть уже записаны. Чтобы быть справедливым, Windows делает хорошую работу по составлению событий таким образом, чтобы вы не были завалены изменениями одного символа и могли не отставать от изменений.
- Обязательно пометьте все определения PInvoke с помощью CharSet = CharSet.Unicode, если они содержат символ где-либо на входе или выходе. PInvoke.net пропустил довольно много из них.
Чистый результат всего этого: приложение-оболочка для консоли Windows API. Оболочка может читать / записывать перенаправленные stdin и stdout для связи с миром. Конечно, если вы хотите получить фантазию, вы можете использовать любой поток здесь (с именем pipe, tcp / ip и т. Д.). Я реализовал несколько управляющих последовательностей xterm и сумел получить полностью работающую оболочку терминала, которая должна быть способна обернуть любой процесс консоли Windows, преобразовать входные данные xterm во входные данные на входе консоли целевого приложения и обработать вывод приложения в управляющие последовательности xterm. Я даже заставил мышь работать. Запуск powershell.exe в качестве подпроцесса теперь решает исходную проблему запуска powershell в сеансе ssh. Cmd.exe также работает. Если кого-то заинтересует, я посмотрю о том, чтобы выложить где-нибудь полный код.