Проблема с использованием CreatePseudoConsole для создания канала pty в python

Я пытаюсь сделать pty pipe. Для этого я должен использовать функцию CreatePseudoConsole из Windows API. Я свободно копирую это, которое это, но на питоне.

Я не знаю, актуально ли это, но я использую Python 3.7.9 и Windows 10.

Это мой код:

from ctypes.wintypes import DWORD, HANDLE, SHORT
from ctypes import POINTER, POINTER, HRESULT
import ctypes
import msvcrt
import os


# The COORD data type used for the size of the console
class COORD(ctypes.Structure):
    _fields_ = [("X", SHORT),
                ("Y", SHORT)]


# HPCON is the same as HANDLE
HPCON = HANDLE


CreatePseudoConsole = ctypes.windll.kernel32.CreatePseudoConsole
CreatePseudoConsole.argtypes = [COORD, HANDLE, HANDLE, DWORD, POINTER(HPCON)]
CreatePseudoConsole.restype = HRESULT


def create_console(width:int, height:int) -> HPCON:
    read_pty_fd, write_fd = os.pipe()
    read_pty_handle = msvcrt.get_osfhandle(read_pty_fd)

    read_fd, write_pty_fd = os.pipe()
    write_pty_handle = msvcrt.get_osfhandle(write_pty_fd)

    # Create the console
    size = COORD(width, height)
    console = HPCON()

    result = CreatePseudoConsole(size, read_pty_handle, write_pty_handle,
                                 DWORD(0), ctypes.byref(console))
    # Check if any errors occured
    if result != 0:
        raise ctypes.WinError(result)

    # Add references for the fds to the console
    console.read_fd = read_fd
    console.write_fd = write_fd

    # Return the console object
    return console


if __name__ == "__main__":
    consol = create_console(80, 80)
    print("Writing...")
    os.write(consol.write_fd, b"abc")
    print("Reading...")
    print(os.read(consol.read_fd, 1))
    print("Done")

Проблема в том, что он не может читать из канала. Я ожидал, что он напечатает "a", но он просто застрял на os.read. Обратите внимание, что я впервые использую WinAPI, поэтому проблема, скорее всего, в нем.


person TheLizzard    schedule 29.03.2021    source источник
comment
Имейте в виду, что это очень новый API Win32, и даже не все версии Windows 10 поддерживают его.   -  person Ben Voigt    schedule 29.03.2021
comment
@BenVoigt Я понятия не имел, но я в основном использую этот проект для своего компьютера. Использует ли он Win64 API (просто догадываюсь)? Также я знаю, что Windows обладает удивительной обратной совместимостью. Разве это не означает, что Win32 API должен поддерживаться всеми версиями Windows 10?   -  person TheLizzard    schedule 29.03.2021
comment
Все версии Windows до 90-х годов используют некоторую форму Win32 API. Но новые версии добавляют в API новые функции. Обратная совместимость означает, что никакие функции не удаляются. Эта конкретная функция была добавлена ​​в Windows 10 October 2018 Update (версия 1809), как показано на страница официальной документации. Если вы работаете только на своем компьютере и на нем установлена ​​версия Windows 10, по крайней мере, с этим обновленным компонентом, вы можете использовать его.   -  person Ben Voigt    schedule 29.03.2021
comment
@BenVoigt О, вот что ты имеешь в виду. Я знаю, что функция CreatePseudoConsole довольно новая (с 2018 года), и поэтому у меня так много проблем с ней. Думаю, со мной все будет хорошо, так как вряд ли я перейду на другие версии Windows. Есть ли у вас какие-либо идеи относительно того, что может быть причиной моей проблемы?   -  person TheLizzard    schedule 29.03.2021
comment
Я не знаю реализацию os.pipe в python, но я думаю, что проблема вызвана дескриптором чтения и записи канала, возможно, вы можете обратиться к: Не удается ввести данные в новую консоль как дочерний процесс в c   -  person Song Zhu    schedule 30.03.2021
comment
@SongZhu-MSFT Я отредактировал свой вопрос с помощью кода, который использовал для удаления вызовов библиотеки os. Это все еще не работает.   -  person TheLizzard    schedule 30.03.2021
comment
После моего теста я обнаружил, что программа заблокирована в ReadFile, я думаю, вы можете обратиться к: Мы отследили канал, и он идет изнутри процесса!. И, судя по официальному образцу, CreatePseudoConsole` не устанавливает связь между двумя каналами. Так почему же необходимо использовать этот API?   -  person Song Zhu    schedule 31.03.2021
comment
@SongZhu-MSFT Я уже знал, что трубы не подключены должным образом, поэтому и задал вопрос. Также, как я уже сказал: Я пытаюсь создать канал pty. Для этого мне нужно использовать CreatePseudoConsole. В Windows нет другого способа создания канала pty в соответствии с это.   -  person TheLizzard    schedule 31.03.2021
comment
Опечатка: argtypes, а не argtype.   -  person user3840170    schedule 04.04.2021
comment
@ user3840170 Это просто для аннотации. Я не думаю, что это имеет значение.   -  person TheLizzard    schedule 04.04.2021
comment
Насколько я помню, ctypes выполняет проверку типов и/или преобразования на их основе. Даже если не критично, думаю стоит исправить.   -  person user3840170    schedule 04.04.2021
comment
В любом случае, на мой взгляд, основная проблема заключается в том, что вы и пишете, и читаете (в терминах Unix) на «главной» стороне PTY, в то время как на «ведомой» стороне нет программы, делающей обратное. Вы пишете в один канал, чей конец для чтения должен удерживаться программой, и читаете из другого канала, чей конец для записи должен поддерживаться программой, но на другом конце ни одного из каналов нет программы.   -  person user3840170    schedule 04.04.2021
comment
Попробуйте перевести пример в ‹блоги разработчиков .microsoft.com/commandline/› или ‹docs.microsoft.com/en-us/windows/console/› и посмотрите, к чему это приведет.   -  person user3840170    schedule 04.04.2021
comment
@user3840170 user3840170 Разве главная сторона не read_pty_fd и write_fd, а подчиненная сторона read_fd и write_pty_fd? Из моей функции я возвращаю read_fd и write_fd. Кроме того, именно так они сделай это. Исправление орфографической ошибки ничего не дало.   -  person TheLizzard    schedule 04.04.2021
comment
Нет, главная сторона — это read_fd и write_fd, а подчиненная сторона — это read_pty_fd и write_pty_fd; последние два преобразуются в пару HANDLE и как таковые передаются в вызов CreatePseudoConsole.   -  person user3840170    schedule 04.04.2021
comment
@user3840170 user3840170 Итак, что я должен передать CreatePseudoConsole и куда я должен читать/писать?   -  person TheLizzard    schedule 04.04.2021
comment
CreatePseudoConsole в порядке. Вы передаете полученный от него дескриптор HPCON дочернему процессу (как говорят вам ссылки MSDN), чтобы подключить его к ведомой стороне, в то время как вы используете read_fd и write_fd для связи с ним на ведущей стороне.   -  person user3840170    schedule 04.04.2021
comment
Давайте продолжим обсуждение в чате.   -  person TheLizzard    schedule 04.04.2021


Ответы (1)


В коде нет ничего плохого: вы ошиблись в своих ожиданиях.

То, что вы делаете, это запись в канал, предназначенный для подачи «клавиатурного» ввода в программу, и чтение из другого канала, который возвращает «экранный» вывод из программы. Но на другом конце канала нет реальной программы, и поэтому вызов read ничего не может вернуть.

Дескриптор HPCON, возвращаемый CreatePseudoConsole API, должен быть передан в списке атрибутов потока вновь порожденному процессу через CreateProcess. Кроме этого с ним практически ничего нельзя сделать. (Вы даже не можете подключиться к псевдоконсоли, как это было бы возможно в Unix.) После передачи дескриптора таким образом вы можете взаимодействовать с процессом, используя дескрипторы read_fd и write_fd.

В статье на MSDN приведен полный пример в C создает псевдоконсоль и передает ее новому процессу; то же самое делается самим источником вы связались.

person user3840170    schedule 05.04.2021
comment
Я заработал. Я могу обрабатывать большинство символов ANSI, но у меня есть две проблемы. Если дочерний процесс пишет в stderr, почему консоль не выводит символ ANSI, чтобы изменить цвет на красный? Также я продолжаю видеть, что консоль пишет ESC[nX, где n — число. Что это за персонаж? - person TheLizzard; 07.04.2021
comment
docs.microsoft.com/en-us/windows/ console/ закрывает его. - person user3840170; 07.04.2021
comment
Спасибо. Я использовал википедию, что мне не помогло. И последний вопрос: почему PseudoConsole не окрашивается в тексте, написанном для stderr? У cmd тоже такая проблема. - person TheLizzard; 07.04.2021