菜鸟科技网

C语言如何注册ActiveX控件?

  1. 准备工作:搭建开发环境。
  2. 创建项目:使用 Visual Studio 创建 ATL 项目。
  3. 添加控件:向项目中添加一个 ATL Simple Control。
  4. 实现功能:为控件添加属性、方法和事件。
  5. 编译与注册:编译项目并自动注册控件。
  6. 测试控件:在容器(如 VB6 或 测试容器)中测试控件。
  7. 打包与分发:创建安装程序以便在其他机器上部署。

第 1 步:准备工作

你需要安装 Visual Studio,为了开发 COM/ATL 组件,建议安装 “使用 C++ 的桌面开发” 工作负载,它包含了 ATL 库和相关的模板。

C语言如何注册ActiveX控件?-图1
(图片来源网络,侵删)

第 2 步:创建 ATL 项目

  1. 打开 Visual Studio。
  2. 选择“创建新项目”。
  3. 在模板搜索框中输入 "ATL",然后选择 "ATL 项目"
  4. 为你的项目命名,MyActiveXControl,并选择一个位置。
  5. 点击“创建”。

在 ATL 项目向导中:

  • 应用程序类型:选择 "动态链接库 (DLL)",这是最常见的方式。
  • 其他选项:暂时保持默认设置即可。

点击“完成”。

你已经创建了一个空的 ATL DLL 项目,它可以作为一个 COM 服务器运行。


第 3 步:添加 ATL Simple Control

  1. “解决方案资源管理器” 中,右键点击你的项目名称。
  2. 选择 “添加” -> “类...”
  3. 在“添加类”对话框中,从左侧的模板列表中选择 “ATL”
  4. 在右侧的模板列表中,选择 “ATL Simple Control”
  5. 点击“添加”。

ATL Control 向导 将会打开,你需要在这里配置你的控件。

C语言如何注册ActiveX控件?-图2
(图片来源网络,侵删)
  • 短名称:输入一个简短的名称,MyCtrl,向导会自动根据这个名称生成其他相关的 ID,如 MyCtrlLibIMyCtrl 等。
  • ProgID:这是程序matic ID,如 MyActiveXControl.MyCtrl.1,其他应用程序可以通过这个 ID 来创建你的控件实例,通常保持默认即可。
  • 类型
    • 标准控件:创建一个标准的窗口控件。
    • 复合控件:可以包含标准 Windows 控件(如按钮、文本框)。
    • 微型控件:没有自己的窗口,轻量级。
    • 默认选择“标准控件”
  • 线程模型
    • 单线程:不推荐。
    • 公寓:适用于有用户界面的控件,是大多数情况下的选择。
    • 自由线程:适用于无 UI 的后台服务。
    • 中性线程:适用于可重入的组件。
    • 默认选择“公寓”
  • 接口
    • 双接口:同时支持 IDispatch(用于脚本语言)和 v-table(用于 C++ 等高性能语言)。强烈推荐选择此项,因为它兼容性最好。
    • 仅自定义:只支持 v-table,性能高但脚本无法调用。
    • 仅调度:只支持 IDispatch,性能较低但易于脚本调用。
  • 聚合:保持默认“否”。
  • 支持 ISupportErrorInfo:如果需要通过 ErrorInfo 机制返回详细的错误信息,则勾选,可以暂时不选。
  • 支持连接点:如果你的控件需要触发事件(例如按钮被点击),则必须勾选此项。强烈推荐勾选
  • 支持异步下载:用于从 URL 加载数据,不相关。

配置完成后,点击“完成”。

向导会自动为你生成大量的代码,包括:

  • 一个 C++ 类 CMyCtrl,继承自 CAxWindowIMyCtrl
  • IDL (Interface Definition Language) 文件 (.idl),定义了控件的接口。
  • 资源文件 (.rc),包含控件的默认图标和位图。
  • 注册表脚本文件 (.rgs),用于在注册表中添加和删除 COM 组件信息。

第 4 步:实现功能

现在我们来为控件添加一个简单的属性、方法和事件。

添加一个属性 (Property)

属性是控件的“数据成员”,一个按钮控件的 Caption 属性。

C语言如何注册ActiveX控件?-图3
(图片来源网络,侵删)
  1. 打开 MyCtrl.h 文件。

  2. CMyCtrl 类的定义中,添加一个私有成员变量来存储属性的值:

    // MyCtrl.h : Header file for the CMyCtrl ActiveX Control class.
    // ...
    class ATL_NO_VTABLE CMyCtrl : 
        public CComObjectRootEx<CComSingleThreadModel>,
        public CComCoClass<CMyCtrl, &CLSID_MyCtrl>,
        public IConnectionPointContainerImpl<CMyCtrl>,
        public IConnectionPointImpl<CMyCtrl, &IID__IMyCtrlEvents>,
        public IPersistStreamInitImpl<CMyCtrl>,
        public IOleControlImpl<CMyCtrl>,
        public IOleObjectImpl<CMyCtrl>,
        public IOleInPlaceActiveObjectImpl<CMyCtrl>,
        public IViewObjectExImpl<CMyCtrl>,
        public IViewObject2Impl<CMyCtrl>,
        public OLECMNDEXECUTE_IMPL,
        public IProvideClassInfo2Impl<&CLSID_MyCtrl, &DIID__IMyCtrlEvents, &LIBID_MyActiveXControlLib>,
        public ISupportErrorInfo,
        public IObjectSafetyImpl<CMyCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA>,
        public CStockPropImpl<CMyCtrl, long, &CLSID_MyCtrl, &dispidBackColor>,
        public CStockPropImpl<CMyCtrl, OLE_COLOR, &CLSID_MyCtrl, &dispidForeColor>,
        public CStockPropImpl<CMyCtrl, BSTR, &CLSID_MyCtrl, &dispidFont>,
        public CStockPropImpl<CMyCtrl, CString, &CLSID_MyCtrl, &dispidText>,
        public CComControl<CMyCtrl>,
        public IMyCtrl, // <-- 这是我们自定义的接口
        public IDispatchImpl<IMyCtrl, &IID_IMyCtrl, &LIBID_MyActiveXControlLib>
    {
    // ... (其他代码)
    private:
        long m_MyProperty; // 添加一个私有变量来存储属性值
    public:
        // ... (其他代码)
    };
  3. IMyCtrl 接口定义中(通常在 .idl 文件中)添加属性的 getset 方法,向导通常会为你创建一个示例属性,我们可以修改它或添加新的。 打开 MyCtrl.idl 文件,找到 IMyCtrl 接口定义:

    // MyCtrl.idl : IDL source for MyCtrl.dll
    // ...
    [
        object,
        uuid(....), // 一个唯一的 GUID
        dual,
        helpstring("IMyCtrl Interface"),
        pointer_default(unique)
    ]
    interface IMyCtrl : IDispatch
    {
        [id(1), helpstring("method AboutBox")] void AboutBox();
        [id(2), propget, helpstring("property MyProperty")] HRESULT MyProperty([out, retval] long *pVal);
        [id(2), propput, helpstring("property MyProperty")] HRESULT MyProperty([in] long newVal);
    };

    这里的 [id(2)] 表示 getset 共享同一个 dispatch ID。

  4. MyCtrl.cpp 文件中实现这些方法。

    // MyCtrl.cpp : Implementation of the CMyCtrl class
    // ...
    STDMETHODIMP CMyCtrl::get_MyProperty(long* pVal)
    {
        *pVal = m_MyProperty;
        return S_OK;
    }
    STDMETHODIMP CMyCtrl::put_MyProperty(long newVal)
    {
        m_MyProperty = newVal;
        // 如果属性改变需要触发重绘,可以在这里调用
        // FireViewChange();
        return S_OK;
    }

添加一个方法 (Method)

方法是可以被调用的“函数”。

  1. MyCtrl.idl 文件的 IMyCtrl 接口中添加一个方法:

    interface IMyCtrl : IDispatch
    {
        // ...
        [id(3), helpstring("method SayHello")] HRESULT SayHello([in] BSTR name, [out, retval] BSTR* greeting);
    };
  2. MyCtrl.cpp 中实现它:

    STDMETHODIMP CMyCtrl::SayHello(BSTR name, BSTR* greeting)
    {
        if (!greeting) return E_POINTER;
        // 使用 ATL 的字符串转换宏
        CString strGreeting;
        strGreeting.Format(L"Hello, %s! Welcome to my ActiveX Control.", name);
        // 将结果复制到输出参数
        *greeting = strGreeting.AllocSysString(); // 分配 COM 内存
        return S_OK;
    }

添加一个事件 (Event)

事件是控件向容器“通知”某些事情发生的方式,这需要连接点。

  1. 定义事件接口:在 MyCtrl.idl 文件中,找到 _IMyCtrlEvents 接口定义,这是容器需要实现的接口,用于接收事件。

    [
        uuid(....), // 另一个唯一的 GUID
        helpstring("_IMyCtrlEvents Interface"),
        dispinterface
    ]
    _IMyCtrlEvents
    {
        properties:
        methods:
        [id(1), helpstring("Event OnClick")] void OnClick();
    };

    我们添加了一个 OnClick 事件。

  2. 触发事件:在 MyCtrl.cpp 中,你需要一个宏来触发事件,通常在控件的某个行为(如鼠标点击)中调用。 在 MyCtrl.cpp 的开头,确保包含了 atldef.h。 在需要触发事件的地方(如果你处理了 WM_LBUTTONDOWN 消息),添加以下代码:

    // 在 MyCtrl.cpp 中
    #include <atldef.h>
    // ... 假设这是某个消息处理函数
    LRESULT CMyCtrl::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        // ... 你的处理逻辑
        // 触发 OnClick 事件
        Fire_Click(); // 注意:Fire_ + 事件名称
        return 0;
    }

    Fire_Click 宏是由 ATL 根据连接点接口自动生成的。


第 5 步:编译与注册

  1. 生成解决方案:按 F7 或点击菜单“生成” -> “生成解决方案”。
  2. 自动注册:默认情况下,Visual Studio 会在编译后自动调用 regsvr32.exe 来注册你的 DLL。
    • 如果成功,你会在“输出”窗口看到 DllRegisterServer in C:\...\MyActiveXControl.dll succeeded. 的消息。
    • 如果失败,会显示 failed,检查“错误列表”窗口,通常是代码中有语法错误或 IDL 文件有误。

注册过程会将你的控件信息写入到 Windows 注册表中,这样其他程序(如 IE, VB6)才能找到它。


第 6 步:测试控件

使用 ActiveX Control Test Container

这是 ATL 开发自带的、最方便的测试工具。

  1. 在 Visual Studio 中,点击菜单 “工具” -> “ActiveX Control Test Container”
  2. 在 Test Container 窗口中,点击 “编辑” -> “插入新控件...”
  3. 在列表中,你应该能找到你的控件,名称通常是你在 ProgID 中指定的部分(如 MyCtrl)。
  4. 选中它并点击“确定”,你的控件就会出现在容器窗口中。
  5. 你可以:
    • 右键点击控件,选择“属性”来测试你添加的属性。
    • 使用容器菜单的“调用方法”来测试你的方法。
    • 点击控件,看是否能触发你定义的 OnClick 事件(在 Test Container 中,你需要点击“查看” -> “连接点”,然后选择 _IMyCtrlEvents 并点击“连接”,才能在事件发生时看到消息)。

在 Visual Basic 6.0 中测试

VB6 是一个经典的 ActiveX 容器。

  1. 打开 VB6,创建一个新的“标准 EXE”工程。

  2. 在菜单中选择 “工程” -> “部件...”

  3. 在“控件”选项卡中,浏览并找到你的控件(可能需要点击“浏览”并导航到你的 DLL 文件)。

  4. 勾选你的控件,然后点击“确定”。

  5. 你的控件会出现在工具箱中,你可以像使用其他标准控件一样把它拖到窗体上。

  6. 在 VB6 的属性窗口中,你可以设置和读取你定义的属性。

  7. 在代码窗口中,你可以调用方法和响应事件:

    Private Sub Form_Load()
        ' 调用方法
        Dim greeting As String
        greeting = MyCtrl1.SayHello("VB6 User")
        MsgBox greeting
        ' 设置属性
        MyCtrl1.MyProperty = 123
    End Sub
    Private Sub MyCtrl1_Click()
        ' 响应事件
        MsgBox "You clicked the ActiveX control!"
    End Sub

第 7 步:打包与分发

你编译出的 DLL 文件不能直接复制到其他电脑上使用,因为它依赖于 Visual C++ Redistributable,并且需要注册。

正确的分发方式是创建一个 安装程序 (MSI)

  1. 在 Visual Studio 中,右键点击你的解决方案,选择 “添加” -> “新建项目...”
  2. 搜索并选择 “安装项目”“Visual Studio Installer 项目”(根据你的 VS 版本,名称可能略有不同)。
  3. 为安装项目命名,MyActiveXControlSetup
  4. 在解决方案资源管理器中,右键点击新的安装项目,选择 “添加” -> “项目输出...”
  5. 选择你的 ActiveX 控件项目,并勾选“主输出”,点击“确定”。
  6. 右键点击“主输出”,选择 “创建快捷方式”,并将其拖放到用户的“[程序集]文件夹”中(可选)。
  7. 右键点击安装项目,选择 “视图” -> “注册表”
    • 展开 HKEY_CLASSES_ROOT
    • 找到你的控件的 CLSID(在 MyCtrl_i.c/h 文件中定义),右键点击它,选择 “新建” -> “项”**,命名为 InprocServer32
    • 右键点击新建的 InprocServer32 项,选择 “新建” -> “字符串值”**,命名为 ThreadingModel,并将其值设置为 Apartment
    • 双击 (默认) 值,将其值设置为你的 DLL 文件的完整路径([TARGETDIR]MyActiveXControl.dll)。
  8. 设置自定义操作:为了让安装程序在安装时注册控件,卸载时注销控件。
    • 在安装项目上右键,选择 “视图” -> “自定义操作”**。
    • 在“安装”节点上右键,选择 “添加自定义操作...”
    • 选择“应用程序文件夹”,然后选择你的“主输出”。
    • 对“卸载”节点重复此操作。
  9. 生成安装包:右键点击安装项目,选择 “生成”

你得到了一个 .msi 文件,用户只需双击这个文件,按照向导安装,你的控件就会被正确地复制到系统目录并注册,然后在任何兼容的容器中都可以使用了。

总结与注意事项

  • GUID:每个 CLSID 和 IID 都必须是全局唯一的,向导会自动生成,不要手动修改。
  • 内存管理:在 COM 中,字符串等数据需要使用特定的内存分配函数(如 SysAllocString),ATL 提供了方便的宏(如 CComBSTR)和包装类(如 CStringTAllocSysString 方法)来简化这个过程。
  • 错误处理:使用 HRESULT 作为返回值,如果出错,返回 HRESULT_FROM_WIN32(GetLastError()) 或自定义的错误码,如果勾选了 ISupportErrorInfo,可以通过 SetErrorInfo 提供更详细的错误信息。
  • 线程安全:如果你的控件可能被多线程访问,需要仔细处理同步问题,ATL 的 CComCritSecLock 等工具可以帮助你。
  • 64位 vs 32位:确保你的项目平台(x86, x64, Any CPU)和目标应用程序的平台一致,在 64 位系统上,32 位控件注册在 Wow6432Node 下。

开发 ActiveX 控件是一个复杂但功能强大的过程,遵循以上步骤,你就可以成功地创建、注册和分发自己的 C++ ActiveX 控件了。

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