核心概念
无论使用哪种方法,调用 Windows API 的基本流程都是相似的:

- 找到函数所在的 DLL 文件:Windows API 函数都存储在动态链接库中,
user32.dll(界面相关)、kernel32.dll(系统核心功能)、shell32.dll(Shell 相关)等。 - 声明函数:告诉 Python 这个函数的名称、它所在的 DLL、以及它需要什么类型的参数和返回值。
- 调用函数:像调用普通 Python 函数一样调用它,并传入正确的参数。
- 处理结果:获取并解析函数返回的值。
使用 ctypes (最常用、最直接)
ctypes 是 Python 的标准库之一,它允许你调用 C 语言兼容的动态链接库,这是最基础、最灵活的方法,不需要安装任何第三方包。
优点:
- 无需安装:Python 自带。
- 功能强大:几乎可以调用所有 Windows API。
- 学习曲线平缓:概念相对直接。
缺点:
- 代码冗长:需要手动定义函数原型、数据结构等。
- 容易出错:类型错误(如传错参数类型)会导致程序崩溃。
示例:获取当前窗口的标题
这个例子将调用 user32.dll 中的 GetWindowTextW 函数来获取活动窗口的标题。
import ctypes
from ctypes import wintypes
# 1. 定义函数原型
# 告诉 Python GetWindowTextW 函数在哪里,它的参数和返回类型是什么
user32 = ctypes.WinDLL('user32', use_last_error=True) # use_last_error=True 可以获取错误码
# 定义函数参数和返回值类型
user32.GetWindowTextW.argtypes = [
wintypes.HWND, # 窗口句柄
wintypes.LPWSTR, # 缓冲区指针
ctypes.c_int # 缓冲区最大长度
]
user32.GetWindowTextW.restype = wintypes.INT # 返回值为拷贝的字符数
# 2. 获取当前活动窗口的句柄
# GetForegroundWindow 是 user32.dll 中的另一个函数
GetForegroundWindow = user32.GetForegroundWindow
GetForegroundWindow.argtypes = []
GetForegroundWindow.restype = wintypes.HWND
# 3. 调用函数
hwnd = GetForegroundWindow()
if not hwnd:
print(f"无法获取窗口句柄,错误码: {ctypes.get_last_error()}")
else:
# 创建一个足够大的缓冲区 (256个字符)
buffer_size = 256
buffer = ctypes.create_unicode_buffer(buffer_size)
# 调用 GetWindowTextW
length = user32.GetWindowTextW(hwnd, buffer, buffer_size)
if length > 0:
window_title = buffer.value
print(f"当前活动窗口标题是: {window_title}")
else:
print(f"无法获取窗口标题,错误码: {ctypes.get_last_error()}")
使用 pywin32 (功能最全面)
pywin32 是一个第三方库,它为 Windows 提供了丰富的 Python 接口,它不仅仅是 API 的简单封装,还提供了很多高级对象,win32gui、win32com 等。
优点:
- 高度封装:将复杂的 API 调用简化成了 Pythonic 的函数调用。
- 功能全面:除了 Win32 API,还包含 COM、ADI 等技术。
- 文档和社区支持好:使用广泛,容易找到教程和解决方案。
缺点:
- 需要安装:
pip install pywin32 - 可能版本不匹配:需要确保
pywin32的版本与你的 Python 版本兼容。
示例:同样获取当前窗口的标题
使用 pywin32,代码会简洁得多。

import win32gui
# 获取当前活动窗口的句柄
hwnd = win32gui.GetForegroundWindow()
# 通过句柄获取窗口标题
# GetWindowText 是 win32gui 模块中封装好的函数
window_title = win32gui.GetWindowText(hwnd)
if window_title:
print(f"当前活动窗口标题是: {window_title}")
else:
print("无法获取窗口标题")
可以看到,代码量大大减少,可读性也更高。
使用 pywin32 和 ctypes 的混合方式
有时,pywin32 可能没有封装你需要的特定 API 函数,这时,你可以在 pywin32 的基础上,直接使用 ctypes 来调用未封装的函数。
import win32gui
import ctypes
from ctypes import wintypes
# 我们想调用 user32.dll 中的 GetWindowRect 函数
# pywin32 没有直接提供这个函数,所以用 ctypes
# 1. 定义 ctypes 函数原型
user32 = ctypes.WinDLL('user32', use_last_error=True)
GetWindowRect = user32.GetWindowRect
GetWindowRect.argtypes = [wintypes.HWND, ctypes.POINTER(wintypes.RECT)]
GetWindowRect.restype = wintypes.BOOL
# 2. 使用 pywin32 获取句柄
hwnd = win32gui.GetForegroundWindow()
# 3. 定义 RECT 结构体
class RECT(ctypes.Structure):
_fields_ = [
("left", wintypes.LONG),
("top", wintypes.LONG),
("right", wintypes.LONG),
("bottom", wintypes.LONG)
]
# 4. 调用 ctypes 函数
rect = RECT()
if GetWindowRect(hwnd, ctypes.byref(rect)):
print(f"窗口句柄: {hwnd}")
print(f"窗口位置和大小: 左={rect.left}, 上={rect.top}, 右={rect.right}, 下={rect.bottom}")
width = rect.right - rect.left
height = rect.bottom - rect.top
print(f"窗口宽度: {width}px, 高度: {height}px")
else:
print(f"GetWindowRect 调用失败,错误码: {ctypes.get_last_error()}")
使用 windll (简化版 ctypes)
ctypes 提供了 windll 和 cdll 两个模块。windll 专门用于调用 Windows API(使用 stdcall 调用约定),它比 ctypes.WinDLL 更简洁。
from ctypes import wintypes
# 直接从 windll 导入 user32
user32 = ctypes.windll.user32
# 定义参数和返回值
user32.GetWindowTextW.argtypes = [wintypes.HWND, wintypes.LPWSTR, wintypes.INT]
user32.GetWindowTextW.restype = wintypes.INT
# 获取句柄
hwnd = user32.GetForegroundWindow()
# 调用
buffer = ctypes.create_unicode_buffer(256)
length = user32.GetWindowTextW(hwnd, buffer, 256)
if length > 0:
print(f"当前活动窗口标题是: {buffer.value}")
else:
print("无法获取窗口标题")
进阶:处理复杂参数和结构体
很多 API 函数需要传入结构体指针,RECT、POINT、MSG 等,处理这些需要用到 ctypes.Structure。

示例:模拟鼠标点击
这个例子需要调用 mouse_event 函数,它需要 MOUSEINPUT 结构体。
import ctypes
import time
# 定义常量
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
# 定义结构体 MOUSEINPUT
class MOUSEINPUT(ctypes.Structure):
_fields_ = [
("dx", ctypes.c_long),
("dy", ctypes.c_long),
("mouseData", ctypes.c_ulong),
("dwFlags", ctypes.c_ulong),
("time", ctypes.c_uint),
("dwExtraInfo", ctypes.POINTER(ctypes.ULONG)),
]
# 定义结构体 INPUT (mouse input)
class INPUT(ctypes.Structure):
class _INPUT(ctypes.Union):
_fields_ = [("mi", MOUSEINPUT)]
_anonymous_ = ("_input",)
_fields_ = [
("type", ctypes.c_ulong),
("_input", _INPUT),
]
# 定义函数原型
user32 = ctypes.windll.user32
SendInput = user32.SendInput
SendInput.argtypes = [ctypes.c_uint, ctypes.POINTER(INPUT), ctypes.c_int]
SendInput.restype = ctypes.c_uint
def simulate_click(x, y):
# 设置鼠标位置 (这里假设已经在目标位置,dx, dy 为 0)
# 如果要移动鼠标,需要使用 SetCursorPos
mi = MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=MOUSEEVENTF_LEFTDOWN, time=0, dwExtraInfo=None)
# 创建 INPUT 结构体数组
inputs = (INPUT * 1)(INPUT(type=0, mi=mi)) # 0 表示 MOUSE input
# 发送按下事件
SendInput(1, inputs, ctypes.sizeof(INPUT))
time.sleep(0.1) # 短暂延时,模拟真实点击
# 修改标志为抬起
mi.dwFlags = MOUSEEVENTF_LEFTUP
inputs = (INPUT * 1)(INPUT(type=0, mi=mi))
# 发送抬起事件
SendInput(1, inputs, ctypes.sizeof(INPUT))
# --- 调用 ---
print("将在 3 秒后模拟鼠标左键点击...")
time.sleep(3)
simulate_click(0, 0) # 在当前位置点击
print("点击完成!")
总结与推荐
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
ctypes |
无需安装,功能强大,灵活 | 代码繁琐,易出错 | 快速调用简单 API,或作为学习底层原理的工具。 |
pywin32 |
封装好,代码简洁,功能全面 | 需要安装,版本兼容性 | 绝大多数 Windows 自动化任务的首选。 |
pywin32 + ctypes |
结合两者优势,非常灵活 | 需要同时了解两者 | 当 pywin32 没有你需要的特定 API 时。 |
windll |
比 ctypes.WinDLL 更简洁 |
功能与 ctypes 类似 |
喜欢更简洁写法的开发者。 |
给你的建议:
- 如果你是初学者:直接从
pywin32开始,它能让你用最少的代码完成最多的工作,并且能让你更专注于业务逻辑而不是繁琐的 API 定义。 - 如果你需要调用非常冷门或最新的 API:
pywin32可能没有提供封装,这时你不得不使用ctypes来手动定义函数原型。 - 如果你想深入理解 Windows API 与 Python 的交互:从
ctypes开始是最好的方式,它能让你明白一切工作的底层原理。
