zoukankan      html  css  js  c++  java
  • ATL开发一个ActiveX

    原文地址:http://www.cnblogs.com/chinadhf/archive/2010/09/03/1817336.html

    本文描述了使用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;
    
      //...
    }
  • 相关阅读:
    Android Media Playback 中的MediaPlayer的用法及注意事项(二)
    Android Media Playback 中的MediaPlayer的用法及注意事项(一)
    34. Search for a Range
    33. Search in Rotated Sorted Array
    32. Longest Valid Parentheses
    31. Next Permutation下一个排列
    30. Substring with Concatenation of All Words找出串联所有词的子串
    29. Divide Two Integers
    28. Implement strStr()子串匹配
    27. Remove Element
  • 原文地址:https://www.cnblogs.com/whisht/p/2319341.html
Copyright © 2011-2022 走看看