Использование средств WinAPI в Python: практические сценарии
Основные библиотеки для работы с Windows API из Python
Как вызвать функцию Win32 API напрямую из кода Python?
Для прямого доступа к функциям, экспортируемым из DLL Windows (kernel32, user32, gdi32 и др.), используется встроенная библиотека ctypes. Она позволяет объявлять сигнатуры функций, передавать аргументы нужных типов и получать возвращаемые значения. Это даёт полный контроль над вызовом, но требует знания C-интерфейса.
import ctypes
user32 = ctypes.windll.user32
result = user32.MessageBoxW(0, 'Привет, мир!', 'Информация', 1)
print('Результат:', result)Python lib windows (использование библиотек windows в python)
После выполнения появится окно сообщения. Кнопка 'OK' вернёт 1, 'Cancel' - 2.
Цель использования: когда необходима интеграция с низкоуровневыми функциями Windows, отсутствующими в других библиотеках, или когда нужен минимальный размер зависимостей.
Более высокоуровневая альтернатива - библиотека pywin32 (модуль win32api, win32gui и др.). Она предоставляет обёртки для многих распространённых WinAPI функций, скрывая работу с типами и указателями.
import win32api
import win32con
win32api.MessageBox(0, 'Привет из pywin32', 'Информация', win32con.MB_OK)
Вызов происходит проще, без явного объявления типов. Цель использования: быстрое прототипирование или работа с распространёнными функциями (окна, процессы, реестр) без углубления в C-сигнатуры.
Типичная ошибка: неправильное указание типов аргументов в ctypes приводит к падению интерпретатора или неверным результатам. Например, передача строки без преобразования в c_wchar_p.
Решение: всегда проверять сигнатуру функции в документации MSDN, использовать ctypes.create_unicode_buffer для строк, а для чисел - c_int, c_uint. После вызова использовать ctypes.GetLastError() для диагностики.
В pywin32 ошибки обычно возвращаются как исключения pywintypes.error, что облегчает отладку.
Как получить информацию о запущенных процессах и окнах?
Через ctypes можно получить список процессов с помощью функций Toolhelp32Snapshot, Process32First/Process32Next.
import ctypes
from ctypes import wintypes
kernel32 = ctypes.windll.kernel32
snapshot = kernel32.CreateToolhelp32Snapshot(2, 0)
if snapshot == -1:
raise Exception('Ошибка создания снимка')
class PROCESSENTRY32(ctypes.Structure):
_fields_ = [('dwSize', wintypes.DWORD),
('cntUsage', wintypes.DWORD),
('th32ProcessID', wintypes.DWORD),
('th32DefaultHeapID', ctypes.POINTER(wintypes.DWORD)),
('th32ModuleID', wintypes.DWORD),
('cntThreads', wintypes.DWORD),
('th32ParentProcessID', wintypes.DWORD),
('pcPriClassBase', wintypes.LONG),
('dwFlags', wintypes.DWORD),
('szExeFile', wintypes.CHAR * 260)]
pe = PROCESSENTRY32()
pe.dwSize = ctypes.sizeof(PROCESSENTRY32)
if kernel32.Process32FirstW(snapshot, ctypes.byref(pe)):
while True:
print(f'PID: {pe.th32ProcessID}, Имя: {pe.szExeFile.decode("cp866")}')
if not kernel32.Process32NextW(snapshot, ctypes.byref(pe)):
break
kernel32.CloseHandle(snapshot)
Вывод содержит PID и имя исполняемого файла. Цель: мониторинг системы, поиск конкретных процессов.
pywin32 предоставляет более простые функции: win32process.EnumProcesses(), win32gui.EnumWindows().
import win32process
import win32api
pids = win32process.EnumProcesses()
for pid in pids[:10]:
try:
handle = win32api.OpenProcess(0x0400 | 0x0010, False, pid)
name = win32process.GetModuleFileNameEx(handle, 0)
print(f'{pid}: {name}')
win32api.CloseHandle(handle)
except:
pass
Цель: быстрый обход процессов без низкоуровневой работы со структурами.
Проблема: для доступа к некоторым процессам требуются права администратора. Без этого OpenProcess завершается ошибкой (ERROR_ACCESS_DENIED).
Решение: запускать скрипт с повышенными привилегиями (от имени администратора). В ctypes можно игнорировать неудачные вызовы с помощью try/except.
Как читать и изменять параметры системного реестра?
Встроенный модуль winreg предоставляет функции для работы с реестром Windows. Работает во всех версиях Python без дополнительных установок.
import winreg
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion')
value, regtype = winreg.QueryValueEx(key, 'ProgramFilesDir')
print('ProgramFilesDir:', value)
winreg.CloseKey(key)
Цель: получение системных путей, настроек приложений, установка значений.
pywin32 также имеет обёртки: win32api.RegOpenKeyEx, RegQueryValueEx.
import win32api
import win32con
key = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion', 0, win32con.KEY_READ)
value, regtype = win32api.RegQueryValueEx(key, 'ProgramFilesDir')
print(value)
win32api.RegCloseKey(key)
Цель: единообразный интерфейс с остальными функциями pywin32.
Проблема разрядности: 32-битная программа видит свою ветку реестра (Wow6432Node). Чтобы обратиться к оригинальной, нужно использовать флаг KEY_WOW64_64KEY.
Решение: при открытии ключа в winreg и pywin32 указать sam=win32con.KEY_READ | win32con.KEY_WOW64_64KEY. В winreg можно использовать OpenKey с access=KEY_READ | 0x0100.
Как выполнить внешнюю программу и обработать её вывод?
Самое удобное - subprocess. Он предоставляет функции run, Popen, capture_output.
import subprocess
result = subprocess.run(['ping', 'localhost', '-n', '2'], capture_output=True, text=True, encoding='cp866')
print(result.stdout[:200])
Цель: запуск консольных утилит, получение вывода, контроль за выполнением.
Для более тонкого управления процессом (создание в скрытом окне, задание приоритета) используется ctypes с CreateProcess.
import ctypes
from ctypes import wintypes
kernel32 = ctypes.windll.kernel32
class STARTUPINFO(ctypes.Structure):
_fields_ = [('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', ctypes.c_void_p),
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE)]
class PROCESS_INFORMATION(ctypes.Structure):
_fields_ = [('hProcess', wintypes.HANDLE),
('hThread', wintypes.HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD)]
si = STARTUPINFO()
si.cb = ctypes.sizeof(STARTUPINFO)
si.dwFlags = 0x00000001 # STARTF_USESHOWWINDOW
si.wShowWindow = 0 # SW_HIDE
pi = PROCESS_INFORMATION()
cmd = 'cmd.exe /c dir C:\\'
if kernel32.CreateProcessW(None, cmd, None, None, False, 0x08000000, None, None, ctypes.byref(si), ctypes.byref(pi)):
print('Процесс запущен, PID:', pi.dwProcessId)
kernel32.CloseHandle(pi.hThread)
kernel32.CloseHandle(pi.hProcess)
else:
print('Ошибка:', kernel32.GetLastError())
Здесь процесс запускается скрыто (SW_HIDE). Цель: полный контроль создания процесса, перенаправление потоков (hStdInput и т.д.).
Проблема кодировки: вывод консоли Windows может быть в cp866, cp1251 или другой кодировке в зависимости от настроек. subprocess с параметром encoding='cp866' решает, но не универсально.
Решение: использовать chcp для определения кодовой страницы или декодировать вручную. Для CreateProcess нужно самостоятельно создавать pipes и читать из них.
Как автоматизировать COM-объекты, например, Excel или Word?
comtypes - библиотека для работы с COM (Component Object Model) из Python. Позволяет вызывать методы COM-объектов напрямую.
import comtypes.client
excel = comtypes.client.CreateObject('Excel.Application')
excel.Visible = True
book = excel.Workbooks.Add()
sheet = book.Worksheets(1)
sheet.Cells(1,1).Value = 'Привет из comtypes'
book.SaveAs('C:\\test.xlsx')
excel.Quit()
Цель: автоматизация приложений Office, работы с WMI, ADSI и другими COM-серверами.
pywin32 также имеет модуль win32com.client, который более популярен и имеет обширную документацию.
import win32com.client
excel = win32com.client.Dispatch('Excel.Application')
excel.Visible = True
book = excel.Workbooks.Add()
sheet = book.Worksheets(1)
sheet.Cells(1,1).Value = 'Привет из win32com'
book.SaveAs('C:\\test.xlsx')
excel.Quit()
Цель: то же самое, но часто проще в установке (pip install pywin32).
Проблема: COM-объект не зарегистрирован или используется не та версия (например, Office 64-bit). Возникает исключение COMDispatchError.
Решение: проверить наличие приложения, переустановить, использовать progid целиком (Excel.Application.16), либо использовать comtypes.client.GetModule для загрузки типов вручную.
Расширенные примеры использования библиотек Windows в Python
Пример 1: Перечисление окон с получением заголовков (ctypes)
Используется функция EnumWindows с пользовательским callback. Позволяет получить все окна верхнего уровня и их заголовки.
import ctypes
from ctypes import wintypes
WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
def enum_proc(hwnd, lparam):
length = user32.GetWindowTextLengthW(hwnd) + 1
buffer = ctypes.create_unicode_buffer(length)
user32.GetWindowTextW(hwnd, buffer, length)
if buffer.value:
print(f'HWND: {hwnd}, Title: {buffer.value}')
return True
user32 = ctypes.windll.user32
EnumWindows = user32.EnumWindows
EnumWindows(WNDENUMPROC(enum_proc), 0)
HWND: 65552, Title: Program Manager HWND: 197868, Title: Microsoft Visual Studio Code HWND: 852072, Title: Блокнот - ConsoleWindow.txt ... (список всех окон)
Пояснение: WINFUNCTYPE создаёт вызываемую функцию из Python. EnumWindows вызывает её для каждого окна. GetWindowTextW получает текст заголовка. Важно проверять, что buffer.value не пуст, иначе окно без заголовка.
Пример 2: Создание и удаление ключа реестра (winreg)
Работа с реестром: создаём ветку HKCU\Software\MyApp, записываем значение и удаляем.
import winreg
key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, 'Software\\MyApp')
winreg.SetValueEx(key, 'Version', 0, winreg.REG_SZ, '1.0')
winreg.CloseKey(key)
print('Ключ создан')
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\MyApp')
value, _ = winreg.QueryValueEx(key, 'Version')
print('Version =', value)
winreg.CloseKey(key)
import win32api
import win32con
win32api.RegDeleteKey(win32con.HKEY_CURRENT_USER, 'Software\\MyApp')
print('Ключ удалён')
Ключ создан Version = 1.0 Ключ удалён
Пояснение: CreateKey создаёт или открывает существующий ключ. SetValueEx пишет значение. Для удаления используется RegDeleteKey из pywin32. При ошибке доступа требуется запуск от администратора.
Пример 3: Запуск процесса с перенаправлением вывода в памяти (CreateProcess через ctypes)
Создаём процесс cmd.exe, перенаправляем stdout в pipe и читаем вывод без записи на диск.
import ctypes
from ctypes import wintypes
kernel32 = ctypes.windll.kernel32
SA = ctypes.c_void_p
SECURITY_ATTRIBUTES = ctypes.Structure()
ctypes.resize(SECURITY_ATTRIBUTES, ctypes.sizeof(ctypes.c_ulong)*3)
SECURITY_ATTRIBUTES.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
SECURITY_ATTRIBUTES.bInheritHandle = True
SECURITY_ATTRIBUTES.lpSecurityDescriptor = None
hRead, hWrite = wintypes.HANDLE(), wintypes.HANDLE()
kernel32.CreatePipe(ctypes.byref(hRead), ctypes.byref(hWrite), ctypes.byref(SECURITY_ATTRIBUTES), 0)
si = STARTUPINFO()
si.cb = ctypes.sizeof(STARTUPINFO)
si.dwFlags = 0x00000001 | 0x00000100 # STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES
si.wShowWindow = 0 # SW_HIDE
si.hStdOutput = hWrite
si.hStdError = hWrite
pi = PROCESS_INFORMATION()
cmd = 'cmd.exe /c dir C:\\'
kernel32.CreateProcessW(None, cmd, None, None, True, 0x08000000, None, None, ctypes.byref(si), ctypes.byref(pi))
kernel32.CloseHandle(hWrite)
buffer = ctypes.create_string_buffer(4096)
bytes_read = wintypes.DWORD()
kernel32.ReadFile(hRead, buffer, 4096, ctypes.byref(bytes_read), None)
output = buffer.raw[:bytes_read.value].decode('cp866')
print(output[:500])
kernel32.CloseHandle(hRead)
kernel32.CloseHandle(pi.hProcess)
kernel32.CloseHandle(pi.hThread)
Том в устройстве C имеет метку Windows Номер тома: 1234-5678 Содержимое папки C:\ ... (первые 500 символов вывода dir)
Пояснение: создаётся анонимный pipe, дескриптор записи передаётся дочернему процессу, родитель читает из дескриптора чтения. После запуска hWrite закрывается, иначе ReadFile не завершится. Работает без всплывающего окна консоли.
Пример 4: Чтение данных из Excel через comtypes
Открываем существующий файл Excel, читаем значение из первой ячейки.
import comtypes.client
import os
xl = comtypes.client.CreateObject('Excel.Application')
xl.Visible = False
book = xl.Workbooks.Open(os.path.abspath('test.xlsx'))
sheet = book.Worksheets(1)
cell_value = sheet.Range('A1').Value
print('Значение в A1:', cell_value)
book.Close(SaveChanges=False)
xl.Quit()
Значение в A1: Привет из comtypes
Пояснение: comtypes автоматически загружает библиотеку типов. Visible=False ускоряет работу. Range('A1') возвращает COM-объект, его свойство Value содержит данные.