杰瑞科技汇

Python如何开发ActiveX控件?

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

Python如何开发ActiveX控件?-图1
(图片来源网络,侵删)

直接使用 Python 的标准库是无法创建 ActiveX 控件的,因为 ActiveX/COM 是微软的 Windows 平台技术,我们需要借助一些“桥梁”技术。

核心概念:Python 与 COM 的交互

在 Windows 上,Python 有两种主要方式与 COM 交互:

  1. 作为 COM 客户端:Python 调用其他语言(如 C++)编写的 COM 组件,这是最常见和最简单的用法。
  2. 作为 COM 服务器:让 Python 代码本身成为一个 COM 组件,可以被其他语言(如 VB6, C#)调用。创建 ActiveX 控件就是 COM 服务器的一种高级形式。

要实现后者,我们需要一个能让 Python 对象“暴露”给 COM 的库,最著名和功能最强大的库是 pywin32


使用 pywin32 (推荐,功能最强大)

pywin32 是一个 Python 扩展,它提供了对许多 Windows API 的访问,包括完整的 COM 支持,你可以用它来创建 COM 服务器,包括一个带有图形界面的 ActiveX 控件。

Python如何开发ActiveX控件?-图2
(图片来源网络,侵删)

步骤 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 控件,它带有一个按钮,当用户点击按钮时,会弹出一个消息框。

Python如何开发ActiveX控件?-图3
(图片来源网络,侵删)

文件名: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 控件

  1. 注册控件:运行 python activex_demo.py --register
  2. 打开 VB6,创建一个新的 "Standard EXE" 项目。
  3. 添加控件:点击 "Project" -> "Components...",在列表中找到并勾选 "Python.SimpleAXControl"
  4. 放置控件:现在你的工具栏上会出现一个新的控件图标,把它拖到窗体上。
  5. 测试:运行项目,你应该能看到你的控件(一个简化的窗口),点击按钮,应该会弹出一个 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:接口定义更清晰。

基本步骤:

  1. 安装pip install comtypes
  2. 编写代码:代码结构类似,但使用 comtypes 的装饰器和接口定义。
  3. 注册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 调用。


重要注意事项和挑战

  1. Python 解释器依赖:你的 ActiveX 控件依赖于用户的机器上安装了特定版本的 Python,你需要将 Python 运行时打包并一起分发,这会增加部署的复杂性。
  2. 性能:Python 的性能远低于 C++,对于需要高频更新或复杂计算的控件,可能会有性能瓶颈。
  3. UI 开发复杂性:使用 Python 原生创建符合 ActiveX 标准的窗口、处理消息、绘制控件等,是一项非常艰巨的任务,上面的 ControlWindow 示例只是一个极简的演示,真正的控件需要处理大量的细节,如 IOleObject, IOleInPlaceObject, IViewObject 等 COM 接口。
  4. 调试困难:调试一个在 VB6 或 IE 容器中运行的 Python COM 组件比调试一个普通的 Python 脚本要复杂得多。
  5. 现代替代方案:对于新的项目,强烈建议考虑使用 .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 部分的开发,对于新项目,请务必评估是否有更现代、更简单的替代方案。

分享:
扫描分享到社交APP
上一篇
下一篇