Как получить дескриптор дескрипторов Windows STDOUT (в python)?

Я пытаюсь получить доступ к дескрипторам stdout Windows.

Мне нужно понять, почему существуют разные дескрипторы для STDOUT (CONOUT$?) и как интерпретировать эти различия. Я знаю, что Windows API использует разные выходные буферы, но не понял (из многих документов MSDN), когда и как их использовать.

Используя ctypes и python3, я могу получить 2 из них с помощью приведенного ниже сценария. Однако запуск скрипта в (Cygwin) mintty/bash, PowerShell (6.1.1), CMD или даже ConEmu дает несколько разные результаты.

В mintty все дескрипторы stdout одинаковы, а в PowerShell они разные. Что здесь происходит?

Вопрос:
Почему дескрипторы stdout, полученные с помощью: _get_osfhandle и GetStdHandle, различаются?

Вот мой код:

#!/usr/bin/env python3
#-*- coding: utf-8 -*-
#----------------------------------------------------------------------------
import sys
import ctypes
from ctypes import cdll, c_ulong

def color(text, color_code):
    return '\x1b[%sm%s\x1b[0m' % (color_code, text)
def byellow(text): return color(text, '1;49;33')
def printHex(mode):
    return byellow("0x%04x  (%s)" % (mode, mode))   # DWORD

kFile = 'C:\\Windows\\System32\\kernel32.dll'
mFile = 'C:\\Windows\\System32\\msvcrt.dll'

print("\n Getting Console STDOUT handles using 2 different methods:")

try: 
    k32    = cdll.LoadLibrary(kFile)
    msvcrt = cdll.LoadLibrary(mFile)
except OSError as e:
    print("ERROR: %s" % e)
    sys.exit(1)

try: 
    hmsvcrt_osf = msvcrt._get_osfhandle(sys.stdout.fileno())    # Get the parent (?) process Console Output buffer Handle
    hk32_11     = k32.GetStdHandle(-11)                         # Get the current process Console Output buffer Handle
except Exception as e:
    print("ERROR: %s" % e)
    sys.exit(1)

print(" Got stdout handles using:\n")
print("   msvcrt._get_osfhandle : %s" % printHex(hmsvcrt_osf))
print("   k32.GetStdHandle(-11) : %s" % printHex(hk32_11))

Вот результат:

# In PowerShell
# python3.6m.exe .\testHandles.py

 Getting STDOUT handles using 2 different methods:
 Got stdout handles using:

   msvcrt._get_osfhandle : 0x001c  (28)
   k32.GetStdHandle(-11) : 0x014c  (332)

# In Cygwin
# ./testHandles.py

 Getting STDOUT handles using 2 different methods:
 Got stdout handles using:

   msvcrt._get_osfhandle : 0x0338  (824)
   k32.GetStdHandle(-11) : 0x0338  (824)

# pwsh.exe -NoProfile -c "python3.6m.exe C:\test\testHandles.py"

 Getting STDOUT handles using 2 different methods:
 Got stdout handles using:

   msvcrt._get_osfhandle : 0x0338  (824)
   k32.GetStdHandle(-11) : 0x0144  (324)

Согласно MSDN:

  • #P9# <блочная цитата> #P10#
  • #P11# <блочная цитата> #P12#

Связанные вопросы:


ДОПОЛНЕНИЕ: 2018-12-14

In: /usr/lib/python3.6/ctypes/__init__.py:

  • class CDLL(object):

Экземпляр этого класса представляет собой загруженную dll/общую библиотеку, экспортирующую функции с использованием стандартного соглашения о вызовах C (с именем 'cdecl' в Windows).

К экспортированным функциям можно получить доступ как к атрибутам или путем индексирования по имени функции. Примеры:

<obj>.qsort -> вызываемый объект

<obj>['qsort'] -> вызываемый объект

Вызов функций освобождает Python GIL во время вызова и повторно получает его после этого.

  • class PyDLL(CDLL):

Этот класс представляет саму библиотеку Python. Он позволяет получить доступ к функциям Python API. GIL не выпущен, и исключения Python обрабатываются правильно.

Затем if _os.name == "nt":

  • class WinDLL(CDLL):

Этот класс представляет функции экспорта dll с использованием соглашения о вызовах Windows stdcall.


Из комментариев к обсуждению:

stdcall:

Соглашение о вызовах stdcall[4] является разновидностью соглашения о вызовах Pascal, в котором вызываемый объект отвечает за очистку стека, но параметры помещаются в стек в порядке справа налево, как в соглашении о вызовах _cdecl. Регистры EAX, ECX и EDX предназначены для использования внутри функции. Возвращаемые значения хранятся в регистре EAX.

stdcall — это стандартное соглашение о вызовах для Microsoft Win32 API и Open Watcom C++.


person not2qubit    schedule 14.12.2018    source источник
comment
Стандартные дескрипторы не обязательно совпадают с дескрипторами, сопоставленными с файловыми дескрипторами C 0, 1 и 2 (могут быть дубликатами или совершенно не связаны); а стандартные потоки ввода-вывода stdin, stdout и stderr не обязательно являются файловыми дескрипторами 0, 1 и 2. Кроме того, среда выполнения C, используемая Python, вряд ли будет msvcrt.dll — если вы не используете специальную сборки, такой как Cygwin или MSYS (возможно, учитывая имя в стиле Unix python3.6m.exe). Чтобы убедиться, что это то же самое, импортируйте msvcrt; не загружайте его через ctypes.   -  person Eryk Sun    schedule 14.12.2018
comment
kernel32.dll должен быть загружен как ctypes.WinDLL('kernel32', use_last_error=True). Это обеспечивает правильное соглашение о вызовах stdcall для 32-разрядных процессов. use_last_error фиксирует значение последней ошибки потока сразу после вызова, чтобы обеспечить его защиту. Доступ к нему через ctypes.get_last_error().   -  person Eryk Sun    schedule 14.12.2018
comment
Как я описал здесь, я не могу использовать WinDLL (и не импортировать msvcrt), поскольку он не существует в Cygwin Python, АФАЙКТ. Какая разница между windll и cdll? (Также есть pydll.)   -  person not2qubit    schedule 14.12.2018
comment
WinDLL использует соглашение о вызовах stdcall. Нет никакой разницы в 64-битном процессе Windows. Cygwin должен использовать это соглашение внутри себя при вызове функций Windows, но двоичные файлы Cygwin используют только CDLL (cdecl). Я думаю, что общей библиотекой среды выполнения C является cygwin1.dll (отметьте ctypes.util.find_library('c')), но не уместно спрашивать, как файловые дескрипторы Cygwin сопоставляются с дескрипторами Windows. Собственные дескрипторы находятся на другой стороне уровня абстракции ОС, и вы не можете использовать их в программировании POSIX.   -  person Eryk Sun    schedule 14.12.2018