zoukankan      html  css  js  c++  java
  • 使用ATL开发ActiveX控件

    本文描述了使用ATL开发一个ActiveX控件的完整过程。

    一、创建项目

    单击起始页中的“New Project…”,选择“ATL”分类下的“ATL Project”项目,项目名称为“Calculator”。在随后出现的项目向导中,使用默认配置即可。

    image

    二、添加控件

    在解决方案管理器中的项目上右击,依次选择“Add”、“Class”,在添加类对话框中选择ATL分类下的ATL Control类型。单击“Add”按钮,将会出现添加ATL Control向导。

    image 

    image

    在向导的第二步中,将接口类型选择为“Dual”,为控件支持事件做为准备,在Support选项中,选中“Connection points”复选框。

    随后出现选择控件要实现的接口的界面,除VS默认添加的实现外,再添加IObjectSafety接口,实现该接口可以避免控件在IE中使用时IE弹出运行的脚本不安全的提示。

    image

    三、为控件添加并实现方法

    在Class View窗口中右击ICalc接口,依次选择“Add”、“Add Method…”,此处假定我们实现一个加法运算,将方法命名为“Add”,然后添加参数:

    image

    需要注意的是对返回值的处理。应将参数类型选定为DOUBLE*,并选中“retval”复选框。

    向导结束后,VS自动在Calc.cpp中添加了该方法的空实现,略加修改后的方法代码为:

    STDMETHODIMP CCalc::Add(DOUBLE a, DOUBLE b, DOUBLE* result)
    {
    	*result = a + b;
    
    	return S_OK;
    }

    测试该方法:

    由于只是调用该控件进行加法运算,并不需要该控件的界面展示,因此在测试控件之前,可以将VS自动生成的OnDraw方法中的其他代码删除,直接返回 S_OK 即可。

    对VS自动生成的用于测试的htm略做修改来测试添加的方法。修改后的完整htm代码如下:

    <HTML>
    <HEAD>
    <TITLE>ATL 8.0 test page for object Calc</TITLE>
    </HEAD>
    <BODY>
    
    <OBJECT ID="Calc" CLASSID="CLSID:59443E6F-7B99-4F75-A7AF-6FEE5B8208CD"></OBJECT>
    
    <input type="button" value="Add" onclick="add();" />
    
    <script type="text/javascript">
        function add() {
            var calc = document.getElementById('Calc');
            var result = calc.Add(2, 3);
            alert(result);
        }
    </script>
    
    </BODY>
    </HTML>

    点击“Add”按钮后的运行效果:

    image

    四、为控件添加事件

    假定控件进行的是一个非常复杂的运算,为了在调用运算时不阻塞调用者线程,可以使用异步方式完成运算。控件在完成运算时需要通知调用者,这时便需要事件。

    首先按照步骤三中的方法,添加一个异步调用加法运算的方法AddAsync,然后为控件添加运算完成的事件AddCompleted。

    在Class View窗口中右击_ICalcEvents接口,依次选择“Add”、“Add Method…”,根据添加方法向导添加AddCompleted方法,如下图所示:

    image

    然后在Class View窗口中右击CCalc类,依次选择“Add”、“Add Connection Point…”,在弹出的实现连接点窗口中实现_ICalcEvents接口。

    image

    完成向导后,VS会自动为我们生成基本框架,包括引发事件的方法Fire_AddCompleted。我们只需在AddAsync方法中添加运算并在运算结束时调用Fire_AddCompleted的代码:

    STDMETHODIMP CCalc::AddAsync(DOUBLE a, DOUBLE b)
    {
    	double result;
    	result = a + b;
    	Fire_AddCompleted(&result);
    
    	return S_OK;
    }

    在网页中添加异步计算的代码进行测试(添加的javascript代码如下),应该能够得到我们想要的效果。

    <script type="text/javascript">
        function addAsync() {
            var calc = document.getElementById('Calc');
            calc.attachEvent("AddCompleted", OnAddCompleted);
            calc.AddAsync(3, 4);
        }
    
        function OnAddCompleted(result) {
            alert(result);
        }
    </script>

    五、ActiveX控件的事件与多线程

    细心的读者一定会发现步骤四中所谓的“异步”是假的:虽然在运算结束后使用事件对调用者进行通知,但由于运算是在主线程上进行的,所以调用过程仍是同步的。步骤四其实只是展示了一下事件的简单用法,如果真正实现异步,则需要在控件中使用多线程。

    在ActiveX控件中使用多线程时需要注意的是:引发事件(即调用Fire_XXXX)必须在窗口线程中进行,否则会导致事件不能被ActiveX控件的容器处理。如果事件发生时执行代码的线程不是窗口线程。那么应该使用PostMessage或SendMessage来通知窗口线程,并在消息处理函数中执行Fire_XXXX。为了使用控件的消息机制,还应该在CCalc的构造函数中将m_bWindowOnly字段设置为TRUE。

    以下是改为多线程后的部分示例代码:

    STDMETHODIMP CCalc::AddAsync(DOUBLE a, DOUBLE b)
    {
    	m_a = a;
    	m_b = b;
    
    	_beginthreadex(NULL, NULL, AddMethod, this, NULL, NULL);
    
    	return S_OK;
    }
    
    unsigned __stdcall CCalc::AddMethod(LPVOID arg)
    {
    	CCalc* pThis = (CCalc*)arg;
    	pThis->m_result = pThis->m_a + pThis->m_b;
    
    	pThis->SendMessage(WM_ADDCOMPLETED);
    
    	return 0;
    }
    
    LRESULT CCalc::OnAddCompleted(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
    	Fire_AddCompleted(&m_result);
    
    	return 0;
    }

    至此,一个简单的ActiveX控件就开发完成了。关于ActiveX控件的打包部署等问题,可以参考以下内容:

    1、Web发布cab文件打包的ActiveX控件总结

    2、ActiveX控件打包成Cab置于网页中自动下载安装

    3、VS2005下MFC开发的ActiveX控件的部分总结

    本文示例所使用的开发环境为Visual Studio 2010。

    附:使用VC6开发时的注意事项

    在今天看来,VC6显得有些古老,但由于目前能见到的大多数版本的Windows操作系统已经内置了运行VC6开发的应用程序所需要的库,因此从方便发布的角度看,使用VC6来开发ActiveX控件不失为一个好的选择。

    使用VC6开发ActiveX控件与上文所述步骤大同小异,但是需要注意微软给开发者留下的两道试题--使用VC6向导生成的代码中包含两处错误:

    第一处错误位于连接点映射,DIID__IXXXXEvents中的第一个字符‘D’需手动添加。

    示例代码:

    BEGIN_CONNECTION_POINT_MAP(CCalc)
    	CONNECTION_POINT_ENTRY(DIID__ICalcEvents)	// 修改 IID_XXXX 为 DIID_XXXX
    END_CONNECTION_POINT_MAP()

    该错误会造成生成失败,比较容易发现。

    第二处错误位于Fire_XXXX方法内,不会造成生成失败但会造成运行结果莫名其妙,因此该错误更隐蔽一些。

    示例代码:

    if (pConnection)
    {
      CComVariant avarParams[1];
      avarParams[0].byref = result;    //此处自动生成的代码有错误,应去掉原代码中的取址运算
      avarParams[0].vt = VT_R8|VT_BYREF;
      CComVariant varResult;
    
      //...
    }
  • 相关阅读:
    Symbol
    对象的附加属性
    怪异盒模型
    javascript的三大组成部分
    让目标对象滚动到视口位置
    隐藏单个盒子的滚动条
    uni-app判断有没有安装这个app,如果有的话直接打开,没有的话跳转到下载页面
    js 读取json文件
    openlays 使用 svg标注,动态修改svg颜色
    iview table组件内容过多用“...”代替,鼠标悬停显示全部内容
  • 原文地址:https://www.cnblogs.com/chinadhf/p/1817336.html
Copyright © 2011-2022 走看看