Использование средств WinAPI в Python: практические сценарии

Раздел: Системное программирование -> программирование под Windows

Основные библиотеки для работы с 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 содержит данные.

использование библиотек Windows в Python - comments

En
Python lib windows (python)