这是一个非常经典的需求,尤其是在维护旧的 Windows 桌面应用程序(例如用 VB6, Delphi, 或 C++ MFC 开发的)时,ActiveX 本质上是 COM (Component Object Model) 组件的一种特定形式,主要用于在容器(如网页、桌面应用)中嵌入可交互的控件。

直接使用 Python 的标准库是无法创建 ActiveX 控件的,因为 ActiveX/COM 是微软的 Windows 平台技术,我们需要借助一些“桥梁”技术。
核心概念:Python 与 COM 的交互
在 Windows 上,Python 有两种主要方式与 COM 交互:
- 作为 COM 客户端:Python 调用其他语言(如 C++)编写的 COM 组件,这是最常见和最简单的用法。
- 作为 COM 服务器:让 Python 代码本身成为一个 COM 组件,可以被其他语言(如 VB6, C#)调用。创建 ActiveX 控件就是 COM 服务器的一种高级形式。
要实现后者,我们需要一个能让 Python 对象“暴露”给 COM 的库,最著名和功能最强大的库是 pywin32。
使用 pywin32 (推荐,功能最强大)
pywin32 是一个 Python 扩展,它提供了对许多 Windows API 的访问,包括完整的 COM 支持,你可以用它来创建 COM 服务器,包括一个带有图形界面的 ActiveX 控件。

步骤 1:安装 pywin32
pip install pywin32
步骤 2:注册你的 Python 脚本作为 COM 服务器
为了让其他程序能找到你的 Python COM 组件,你需要先注册它。pywin32 提供了一个命令行工具 pythoncom 来完成这个任务。
假设你的 Python 文件名为 activex_demo.py,在命令行中运行:
pythoncom.com_server.registeractivex_server("activex_demo.py")
这会修改你的注册表,将 activex_demo.py 中的类注册为一个 COM 对象。
步骤 3:编写 Python ActiveX 控件代码
这是一个完整的示例,我们将创建一个简单的 ActiveX 控件,它带有一个按钮,当用户点击按钮时,会弹出一个消息框。

文件名:activex_demo.py
import win32com.server.util
import win32com.client
import pythoncom
import sys
from win32com.server import register
from pywin32 import win32gui
# 1. 定义你的 ActiveX 控件类
# 这个类必须继承自 `win32com.server.util.CoClass` 和一个标准的 Python 类
class SimpleAXControl(win32com.server.util.CoClass):
# _reg_clsid_ 是你的控件在 Windows 中的唯一标识符 (GUID)
# 你可以使用 `pythoncom.CreateGuid()` 来生成一个新的
_reg_clsid_ = "{12345678-1234-1234-1234-123456789ABC}"
# _reg_progid_ 是一个友好的名字,用于在 VB6 等工具中引用你的控件
_reg_progid_ = "Python.SimpleAXControl"
# _public_methods_ 列出了可以被外部调用的方法
_public_methods_ = ["ShowMessage"]
def __init__(self):
# 初始化 COM 对象
win32com.server.util.CoClass.__init__(self)
print("SimpleAXControl instance created.")
# 2. 实现一个公共方法
def ShowMessage(self):
"""这个方法可以被容器(如 VB6 窗体)调用"""
import win32api
win32api.MessageBox(0, "Hello from Python ActiveX Control!", "Python Says", 0)
# 3. 创建一个窗口类来作为控件的 UI
# 注意:创建 UI 的方式比较原始,通常我们会封装一个简单的 Win32 窗口
class ControlWindow:
def __init__(self, ax_control):
self.ax_control = ax_control
self.hwnd = None
def create_window(self, parent_hwnd):
# 这是一个非常简化的窗口创建过程
# 实际的 ActiveX 控件 UI 开发非常复杂,需要处理消息循环、绘制、事件等
# 这里仅作示意
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = {win32gui.WM_COMMAND: self.on_command}
wc.hInstance = win32gui.GetModuleHandle(None)
wc.lpszClassName = "PythonAXControl"
class_atom = win32gui.RegisterClass(wc)
self.hwnd = win32gui.CreateWindowEx(
0, "PythonAXControl", "Python AX Control",
win32gui.WS_CHILD | win32gui.WS_VISIBLE,
0, 0, 200, 100,
parent_hwnd, 0, wc.hInstance, None
)
# 创建一个按钮 (这是一个简化的例子,实际控件更复杂)
button_hwnd = win32gui.CreateWindowEx(
0, "BUTTON", "Click Me",
win32gui.BS_PUSHBUTTON | win32gui.WS_CHILD | win32gui.WS_VISIBLE,
10, 10, 80, 30,
self.hwnd, 101, wc.hInstance, None
)
def on_command(self, hwnd, msg, wparam, lparam):
# 检查是否是我们的按钮被点击了 (ID 101)
if win32api.LOWORD(wparam) == 101:
# 调用我们 ActiveX 控件的方法
self.ax_control.ShowMessage()
return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
# 4. 定义 COM 服务器工厂
class SimpleAXControlFactory:
_reg_clsid_ = SimpleAXControl._reg_clsid_
_reg_progid_ = SimpleAXControl._reg_progid_
_reg_desc_ = "A simple Python ActiveX Control"
_reg_threading_ = "both"
def __init__(self):
self._wrap_ = SimpleAXControl
# 5. 注册 COM 服务器
# 当这个脚本被直接运行时,注册服务器
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == "--register":
# 使用更现代的 register 函数
register.UseCommandLine(SimpleAXControlFactory)
print("Control registered successfully.")
else:
# 启动 COM 服务
# 注意:一个真正的 ActiveX 控件需要更复杂的容器来承载它
# 这里只是演示 COM 对象的创建
print("Starting COM server...")
# 在实际应用中,这个进程会由容器(如 IE, VB6)启动和管理
# 这里我们手动创建一个实例来测试
pythoncom.CoInitialize()
control = SimpleAXControl()
pythoncom.CoUninitialize()
print("COM server finished.")
步骤 4:在 VB6 中使用你的 Python ActiveX 控件
- 注册控件:运行
python activex_demo.py --register。 - 打开 VB6,创建一个新的 "Standard EXE" 项目。
- 添加控件:点击 "Project" -> "Components...",在列表中找到并勾选 "Python.SimpleAXControl"。
- 放置控件:现在你的工具栏上会出现一个新的控件图标,把它拖到窗体上。
- 测试:运行项目,你应该能看到你的控件(一个简化的窗口),点击按钮,应该会弹出一个 Python 生成的消息框。
使用 ctypes (底层,不推荐用于 UI)
ctypes 是 Python 的一个外部函数库,它允许你调用 DLL 中的函数,理论上,你可以用 ctypes 直接调用 ole32.dll 等底层 COM API 来创建服务器,但这极其复杂,需要手动处理所有 COM 接口、引用计数、内存管理等,这种方法只适用于创建无 UI 的 COM 自动化对象,不适合创建有图形界面的 ActiveX 控件。
对于 ActiveX 开发,ctypes 是一个错误的选择。pywin32 是唯一可行且相对简单的方案。
使用 comtypes (替代方案)
comtypes 是另一个用于 Python COM 编程的库,它提供了更现代的 Pythonic 接口,并且不依赖于 pywin32 的 C 扩展。
使用 comtypes 的优点:
- 纯 Python:没有 C 扩展,更容易分发和安装。
- 现代 API:接口定义更清晰。
基本步骤:
- 安装:
pip install comtypes - 编写代码:代码结构类似,但使用
comtypes的装饰器和接口定义。 - 注册:
comtypes也提供了注册工具,但通常需要你手动创建一个.reg文件或使用comtypes.server.register。
comtypes 示例 (简化版):
import comtypes
import comtypes.client
from comtypes import server
# 定义你的 COM 类
@comtypes.server.CreateCustomGuid("{...}") # 生成一个新的 GUID
@comtypes.server.RegisterInterface("{...}") # 定义接口 ID
class MyPythonAXControl(comtypes.COMObject):
_reg_progid_ = "Python.ComtypesAXControl"
_reg_clsid_ = comtypes.GUID("{...}") # 使用上面生成的 GUID
_public_methods_ = ["ShowMessage"]
def ShowMessage(self):
import win32api # 你仍然需要 pywin32 来调用 Windows API
win32api.MessageBox(0, "Hello from comtypes!", "Python Says", 0)
if __name__ == '__main__':
# 注册服务器
comtypes.server.register.UseCommandLine(MyPythonAXControl)
# 启动 COM 服务
comtypes.COMObject.exe_server(MyPythonAXControl)
comtypes 在创建 COM 对象方面非常强大,但对于创建复杂的 UI 控件,其文档和社区支持不如 pywin32 成熟,如果选择 comtypes,你仍然需要结合 pywin32 来处理 UI 相关的 Win32 API 调用。
重要注意事项和挑战
- Python 解释器依赖:你的 ActiveX 控件依赖于用户的机器上安装了特定版本的 Python,你需要将 Python 运行时打包并一起分发,这会增加部署的复杂性。
- 性能:Python 的性能远低于 C++,对于需要高频更新或复杂计算的控件,可能会有性能瓶颈。
- UI 开发复杂性:使用 Python 原生创建符合 ActiveX 标准的窗口、处理消息、绘制控件等,是一项非常艰巨的任务,上面的
ControlWindow示例只是一个极简的演示,真正的控件需要处理大量的细节,如IOleObject,IOleInPlaceObject,IViewObject等 COM 接口。 - 调试困难:调试一个在 VB6 或 IE 容器中运行的 Python COM 组件比调试一个普通的 Python 脚本要复杂得多。
- 现代替代方案:对于新的项目,强烈建议考虑使用 .NET WinForms 控件(可以通过
pythonnet在 Python 中调用)或 Electron(使用 HTML/CSS/JS 开发桌面应用)等现代技术,它们更易于开发、部署和维护。
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
pywin32 |
功能最强大,文档和社区支持好,能完整实现 COM 服务器和 UI | 依赖 C 扩展,部署稍复杂,UI 开发依然困难 | 维护旧的 VB6/Delphi 应用,或必须在 Windows 桌面应用中嵌入 Python 逻辑的首选方案。 |
comtypes |
纯 Python,API 现代,易于分发 | 文档相对较少,UI 控件开发支持不如 pywin32 成熟 |
创建无 UI 的 COM 自动化服务器,或作为 pywin32 的替代方案。 |
ctypes |
无需额外库(Python 内置) | 极其复杂,几乎无法用于实际项目 | 仅用于学习和研究 COM 底层原理,不推荐用于开发。 |
如果你确实需要开发 ActiveX 控件,并且必须使用 Python,pywin32 是你的不二之选,请做好面对复杂性的心理准备,特别是关于 UI 部分的开发,对于新项目,请务必评估是否有更现代、更简单的替代方案。
