Я пытаюсь получить доступ к дескрипторам 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# блочная цитата>
Связанные вопросы:
- дескриптор дескриптора окна
- Запустите команду и получите ее stdout, stderr отдельно почти в реальном времени, как в терминале
ДОПОЛНЕНИЕ: 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++.
msvcrt
; не загружайте его через ctypes. - person Eryk Sun   schedule 14.12.2018ctypes.WinDLL('kernel32', use_last_error=True)
. Это обеспечивает правильное соглашение о вызовах stdcall для 32-разрядных процессов.use_last_error
фиксирует значение последней ошибки потока сразу после вызова, чтобы обеспечить его защиту. Доступ к нему черезctypes.get_last_error()
. - person Eryk Sun   schedule 14.12.2018WinDLL
(и не импортироватьmsvcrt
), поскольку он не существует в Cygwin Python, АФАЙКТ. Какая разница междуwindll
иcdll
? (Также естьpydll
.) - person not2qubit   schedule 14.12.2018WinDLL
использует соглашение о вызовах 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