⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csdn_文档中心_activex control and it's container.htm

📁 csdn10年中间经典帖子
💻 HTM
📖 第 1 页 / 共 3 页
字号:
          <TD align=middle bgColor=#003399><FONT color=#ffffff>关键字</FONT></TD>
          <TD width=500>&nbsp;&nbsp;&nbsp;&nbsp;ActiveX Control and it's 
            Container</TD></TR>
        <TR>
          <TD align=middle height=5></TD>
          <TD align=middle width=500></TD></TR>
        <TR>
          <TD align=middle bgColor=#003399 height=10><FONT 
            color=#ffffff>出处</FONT></TD>
          <TD height=10>&nbsp;&nbsp;&nbsp;&nbsp;<A 
            href="http://www.csdn.net/develop/style=" 
            width:400??>style='width:400'</A></TD></TR>
        <TR>
          <TD align=middle height=10></TD>
          <TD height=10></TD></TR></TBODY></TABLE><!--文章说明信息结束//-->
      <TABLE border=0 width=600>
        <TBODY>
        <TR>
          <TD align=left><BR>ActiveX控件和它的容器<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 
            1.COM基础<BR>&nbsp; &nbsp; 2.ActiveX控件及实现<BR>&nbsp; &nbsp; 
            3.ActiveX控件容器及实现<BR>&nbsp; &nbsp; 4.总结<BR><BR>1.COM基础<BR><BR>&nbsp; 
            COM是一种组件开发技术, 它实际上是一种在二进制层上兼容的软件开发方法的规范. COM技术是<BR>与具体的编程语言无关的技术, 
            只要是支持COM开发的开发工具都可以用来进行COM应用开发, 而它们<BR>在二进制上兼容的要求由各个开发工具来实现, 
            绝大部分是由编译器实现的.<BR><BR>&nbsp; &nbsp; COM的基础概念有以下几部分组成,1)接口的定义及实现, 
            2)IUnknown接口, 3)GUID (COM中所<BR>涉及的概念还有很多,具体的可以参阅其他资料 ). 
            下面分别简单的介绍它们.<BR><BR>&nbsp; &nbsp; 1).接口的定义及实现<BR>&nbsp; &nbsp; 
            一个接口实际上就是一组定义了具体的功能的函数的集合, 这些定义没有具体的实现. 接口的定<BR>义类似于C++中的纯虚类定义, 
            它定义了接口函数的返回类型、参数个数及函数的功能, COM组件就是<BR>靠这些接口相互进行通信. 
            一个简单的例子如下.(MFC为我们提供了许多方便的宏用来定义接口, 而<BR>且在一般情况下, 我们是使用IDL或者是ODL来定义接口, 
            而不是使用下面这种形式).<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; &nbsp; &nbsp; 
            &nbsp; &nbsp; interface IStack:IUnknown {<BR>&nbsp; &nbsp; &nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtual void Pop(int* pValue) = 
            0;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
            virtual void Push(int Value) = 0;<BR>&nbsp; &nbsp; &nbsp; &nbsp; 
            &nbsp; &nbsp; };<BR>&nbsp; &nbsp; 上面定义的就是一个简单的接口IStack. 
            它定义了两个方法,并且描述了这两个方法的返回类型<BR>(void), 参数个数和类型, 
            两个函数都用纯虚函数实现,在定义接口的文件里并不需要这两个函数的<BR>具体实现. 一般情况下接口的实现可以通过对接口的继承来完成, 
            但在一个组件实现了多个接口的情<BR>况下MFC采用了嵌套子类的实现方法, 具体情况可以参阅其他文档.<BR><BR>&nbsp; 
            &nbsp; 2).IUnknown接口<BR>&nbsp; &nbsp; 在上面的例子中, 
            IStack从一个叫做IUnknown的接口继承而来, 那么IUnknown接口是一个什么<BR>样的接口呢? 再COM规范中要求, 
            任何一个COM组件必须实现IUnknown接口, 
            IUnknown接口的主要作<BR>用是用来维护COM组件的引用计数和对COM组件实现的接口进行查询, 
            先让我们看一下IUnknown接口的<BR>定义.<BR>&nbsp; &nbsp; &nbsp; &nbsp; <BR>&nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interface IUnknown {<BR>&nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtual void 
            QueryInterface(REFIID riid, void** ppvObject) = 0;<BR>&nbsp; &nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtual HRESULT AddRef() = 
            0;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
            virtual HRESULT Release() = 0;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
            &nbsp; };<BR><BR>&nbsp; &nbsp; 在上面IUnknown接口中, 
            AddRef和Release是用来维护引用计数的. 因为一个COM组件可以同时<BR>为多个应用程序服务, 
            如果没有一种适当的机制来维护COM组件的生存期的话, 那么当一个使用COM组<BR>件的应用程序结束时, 这个组件也会被同时释放掉, 
            那么其他使用这个组件的应用程序就会出现要求<BR>访问的组件不存在的错误.所以COM子系统就是用引用计数来解决这个问题, 
            当一个应用程序要求使用<BR>某个组件时, 它就增加这个组件的引用计数, 当这个应用程序结束时, 它就减少这个组件的引用计<BR>数, 
            当一个组件的引用计数为0时, COM子系统就会释放这个组件.<BR>&nbsp; &nbsp; 
            QueryInterface方法是用来在COM组件中查询一个接口是否被实现的方法, 
            因为每一个接口都拥<BR>有一个能唯一标识它自己的一个ID, 称为IID, 通过传递这IID, 
            我们就可以查询一个接口是否被该<BR>COM组件实现, 
            如果该组件实现了该接口,我们就可以利用QueryInterface方法的第二个参数传回的<BR>值来使用这个接口的方法.<BR><BR>&nbsp; 
            &nbsp; 3).GUID<BR>&nbsp; &nbsp; 上面提到, 每个接口都由一个唯一标识自己的ID, IID, 
            同样每个实现了某个接口的C++类也有一<BR>个ID, 称为CLSID, 在OLE Automation中, 
            广泛使用了一种称为类型库的技术, 一个类型库包含了一<BR>个COM组件中所有的类型信息, 包括它实现的接口, 枚举类型, 接口的方法, 
            及接口参数等一些相关<BR>的信息, 同样类型库也是用一个表示自己的ID, LIBID. 
            COM子统为了能在众多的COM技术中尽快的找<BR>的某个类型的COM组件, 又对COM组件进行了分类管理, 
            而每个类又有一个类别ID,CATID, 实际上我<BR>们可以利用这个CATID来列出系统中的所有的控件(CATID_Control). 
            上面说的所有这些ID, 实际上<BR>是一种类型, GUID. 它们只不过是GUID的不同的typedef.<BR>&nbsp; 
            &nbsp; GUID是一种利用系统时间和网卡具有的唯一编号的特性生成的一个具有128位的数字. 
            这个数字<BR>在时间和空间上保证了它的唯一性. 所以接口及相关的一些概念都利用GUID来进行区分, 
            而不是利用<BR>它们的名字.<BR><BR>2.ActiveX控件及实现<BR><BR>&nbsp; &nbsp; 
            ActiveX控件的最早原型应该是随着VB出现的VBX控件, 
            由于VBX控件的16位结构并不能适应32位<BR>操作系统的要求,于是就诞生了OCX控件, OCX控件是一种32位的自包含的简单应用, 
            它实际上是一组<BR>完成指定的功能函数集合.它实际上是DLL的另外一种表现形式. OCX控件可以有自己的界面,也可以没<BR>有界面, 
            它拥有属性, 方法, 而且一个OCX控件可以触发出某种类型的事件, 
            用来通知容器它的状态<BR>的改变或者是某种外部状态的改变或事件的发生, 实现一个OCX控件必须实现一系列既定的接口, 
            这<BR>使得OCX控件显得有些庞大和冗余, 因为有些控件只需要实现这些接口的一部分, 而且对于Internet<BR>来说, 
            实现这些多余的接口无疑增加了控件的体积.所以在1996年PDC大会上, 微软提出了它的<BR>Activate Internet的概念, 
            并把它的一些技术改称为ActiveX技术, ActiveX控件就在原先的OCX控<BR>件上经过对要实现的接口的削减而诞生了, 
            现在只要一个COM组件实现IUnknown接口就可以被称为<BR>ActiveX控件. 
            所以可以说一个ActiveX控件就是一个实现了IUnknown接口并且支持自注册的简单的<BR>COM组件.<BR><BR>&nbsp; 
            &nbsp; 但是实现一个IUnknown接口的控件显然是没有实际用处的, 
            所以真正的ActiveX控件还是要实现<BR>原先OCX控件定义的一些接口, 用来和它的容器进行交互操作. 
            下面简要的说明一下一个真正的<BR>ActiveX控件的实现.除了IUnkown接口外, 
            一个ActiveX控件一般要实现下面接口中的一部分.&nbsp; &nbsp; 
            <BR>IOleObject,IOleInPlaceObject,IOleInPlaceActiveObject,IOleControl, 
            <BR>IDataObject,IViewObject2, IDispatch, IConnectionPointContainer, 
            ProviderClassInfo<BR>[2], ISpecifyPropertyPages, 
            IPerPropertyBrowsing, IPersistStream, 
            <BR>IPersistStreamInit,IPersistMemory, 
            IPersistStorage,<BR>IPersistMoniker, 
            IPersistPropertyBag,IOleCache[2],IExternalConnection,IRunnableObject, 
            IClassFactory实现要求可以查看MSDN.<BR><BR><BR>&nbsp; &nbsp; 
            一个ActiveX控件通常具有一些属性和事件.控件的属性一般情况下是通过IDispatch接口实现<BR>的.在定义相应的控件属性时, 
            有一个被称为DISPID的值,这个值是用来被其他使用该控件的容器调用<BR>属性时使用的, 
            因为它们必须通过IDispatch接口的Invoke方法来调用相应的属性.IDispach的方法Invoke是用来调用响应的属性的关键方法,但是这个方法在调用控件的属性时, 
            并不是用属性的名<BR>字, 而是被称为DISPID的ID值. 在一般情况下, 一个控件通常有它自己的类型库, 
            容器通过查询控件<BR>的类型库得到相应的属性和方法及事件的列表, 
            并取得它们的DISPID,然后就可以通过Invoke方法来<BR>操作它们.<BR><BR><BR>&nbsp; &nbsp; 
            一个ActiveX控件一般具有三种属性, 固有属性(stock property), 环境属性(ambient 
            <BR>property), 自定义属性(custom property). 固有属性是大部分ActiveX控件具有的属性, 
            比如前<BR>景色, 字体等, 环境属性是控件处于容器中时, 有容器提供的一些属性, 如LocaleID, UserMode. 
            <BR>这些属性具有固定的DISPID值, 在控件中可以通过GetAmbientxxxx方法得到这些属性的值. 
            自定义<BR>属性是一个控件要实现自己的某些特定的功能特征时,定义的一些属性, 在容器中这些属性可以通过类<BR>型库来得到, 
            通过对IDispatch接口的调用来处理.<BR><BR><BR><BR>&nbsp; &nbsp; 
            控件的事件是由控件触发的一个消息或通知, 如果一个控件支持事件, 
            它必须实现<BR>IConnectionPointContainer和IConnectionPoint接口, 然后控件定义自己的出接口, 
            这个接口一<BR>般是通过用dispinterface声明, 在容器对控件进行事件响应时, 
            必须使用IDispatch接口的Invoke<BR>方法进行处理, 
            根据Invoke调用传进来的DISPID我们就可以知道是控件触发了哪一个事件, 根据其他<BR>信息, 
            我们就可以对这个事件进行处理.<BR><BR><BR><BR>&nbsp; &nbsp; 
            下面简单介绍一下如何利用MFC来进行ActiveX控件的开发. 首先我们使用AppWizard来生成<BR>ActiveX控件的框架, 
            实际上这个框架已经是一个完整的控件, 
            在向导的帮助下这个控件已经实现了<BR>上面提到的ActiveX控件要实现的接口的一部分重要的接口, 
            象对事件的基本支持,属性的支持.我们<BR>可以在这个框架的帮助下添加我们自己要实现的功能, 
            为这个控件添加属性方法和事件.VC中的<BR>ClassWizard在这方面提供大量的方便的操作, 
            在ClassWizard的AcitveX 
            Automation页提供了对<BR>ActiveX控件的属性事件方法的添加.<BR><BR><BR>&nbsp; &nbsp; 
            对于一个ActiveX控件来说你需要首先弄清楚哪些是要在控件中完成,哪些是要在容器中实现.那<BR>么,需要控件完成的你就要考虑用属性或者是方法来实现,而需要容器来完成的你只需将参数通过事件<BR>触发传递给容器,在容器端来实现.<BR><BR>&nbsp; 
            &nbsp; &nbsp; <BR>&nbsp; &nbsp; 
            另外,一个比较实际的问题是你的控件将是什么样子.比较简单的方法是在ClassWizard的时候指<BR>定控件将继承自那个类,从而拥有该类的外观.但这种方法不够灵活.如果你想定做控件的外观,那么最<BR>好的方法还是你自己手绘控件,或者是通过在控件内部添加一些控件形成组合控件.你可以在OnDraw<BR>(CDC* 
            pdc, const CRect&amp; rcBounds, const CRect&amp; 
            rcInvalid)中来绘制控件.该函数负责控件<BR>的绘制,其中pdc是当前系统用的环境设备,rcBounds是当前控件的rect范围,你可以用它来定位.绘制<BR>控件还是比较简单的,但前提是你必须要了解Windows的绘图机制.主要是会使用CBrush,CDC,CFont等<BR>MFC的基本绘图类.<BR><BR>&nbsp; 
            &nbsp; <BR>&nbsp; &nbsp; 实际上对于ActiveX 
            Control来说,在对它编程完全可以像是对一般的程序一样使用各种MFC的<BR>类,但是很多的类将不得不动态的创建,因此你必须掌握好定位.主要是掌握好对各种子类的重绘和刷新<BR>的时机和方法.<BR><BR><BR>&nbsp; 
            &nbsp; 
            关于属性表的创建,属性表允许控件显示它的各种属性,以供察看和编辑.属性表通常以表的对话<BR>框的形式实现.你可以在这里改变一些控件的属性.对于大多数的控件来说这已经足够了. 
            <BR><BR><BR>&nbsp; &nbsp; 下面我们来看看VC的AppWinzard 
            Control都为我们做了些什么.用VC的AppWinzard Control你<BR>可以快速生成一个ActiveX 
            Control在这里VC自动为我们声称了两个接口:一个用来负责属性和方法.<BR>另一个用来负责事件.这个控件可以在容器运行,但是它什么也不做.并且它的外观也非常简陋.首先让<BR>我们来重新绘制它的外观,这在OnDraw中完成. 
            <BR><BR><BR><BR>//&nbsp; 设置当前的字体,并保留原字体<BR>&nbsp; CFont* 
            pOldfont;<BR>&nbsp; pOldfont = 
            SelectFontObject(pdc,m_customfont);<BR><BR>//&nbsp; 
            得到当前的各种颜色.其中TranslateColor是为了把OLE_COLOR转换成COLORREF.<BR>&nbsp; 
            COLORREF textbkcolor = ::GetSysColor(COLOR_BTNFACE);<BR>&nbsp; 
            COLORREF textforecolor = 
            this-&gt;TranslateColor(this-&gt;GetForeColor());<BR><BR>&nbsp; 
            COLORREF edgebkcolor = ::GetSysColor(COLOR_3DFACE);<BR>&nbsp; 
            COLORREF edgeforecolor = ::GetSysColor(COLOR_3DFACE);<BR><BR>&nbsp; 
            COLORREF oldbkcolor = pdc-&gt;SetBkColor(textbkcolor);<BR>&nbsp; 
            COLORREF oldforecolor = 
            pdc-&gt;SetTextColor(textforecolor);<BR><BR>&nbsp; 
            if(m_brush.m_hObject = NULL)<BR>&nbsp; 
            m_brush.CreateSolidBrush(textbkcolor);<BR>&nbsp; CBrush* pOldbrush = 
            pdc-&gt;SelectObject(&amp;m_brush);<BR><BR>&nbsp; 
            pdc-&gt;Rectangle(&amp;rcBounds);<BR><BR>&nbsp; CSize osize = 
            pdc-&gt;GetTextExtent(m_cstrCaption);<BR>&nbsp; m_size = 
            osize;<BR><BR>&nbsp; 
            pdc-&gt;ExtTextOut((rcBounds.right-osize.cx)/2,(rcBounds.bottom-osize.cy)/2,ETO_CLIPPED&brvbar;ETO_OPAQUE,rcBounds,<BR>&nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
            m_cstrCaption,m_cstrCaption.GetLength(),NULL);&nbsp; &nbsp; 
            <BR>&nbsp; <BR>&nbsp; UINT borderstyle = EDGE_RAISED;<BR>&nbsp; UINT 
            borderflags = BF_RECT;<BR><BR>//&nbsp; 画边框<BR>&nbsp; 
            pdc-&gt;SetBkColor(edgebkcolor);<BR>&nbsp; 
            pdc-&gt;SetTextColor(edgeforecolor);<BR><BR>&nbsp; 
            pdc-&gt;DrawEdge((LPRECT)(LPCRECT)rcBounds,borderstyle,borderflags);<BR><BR>//&nbsp; 
            恢复设置<BR>&nbsp; pdc-&gt;SetBkColor(oldbkcolor);<BR>&nbsp; 
            pdc-&gt;SetTextColor(oldforecolor);<BR><BR>&nbsp; 
            pdc-&gt;SelectObject(pOldfont);<BR>&nbsp; 
            pdc-&gt;SelectObject(pOldbrush);<BR>以上代码将为控件绘制一个比较好的外观,当然你可以任意改变直到你满意为止.以后你就可以根据需<BR>要来添加一些属性和方法并写出相对的实现.大部分的定义都是由CLASS 
            WINZARD来维护的,所以你可<BR>以轻松的添加它们.<BR><BR>&nbsp; &nbsp; 
            一些建议:在控件的属性页的编写过程中,需要将属性页上的标准控件与ActiveX控件的属性相联<BR>系,这样当你在动态的改变标准控件的值时,ActiveX控件的属性会随之改变.但问题是如果你使用别的<BR>方法来动态改变属性页上的标准控件的值,则ActiveX控件的属性不会随之改变.<BR><BR>原因很简单,ActiveX控件的属性不知道自己已经发生改变,所以没有接受从标准控件传来的值.这一过<BR>程是在DoDataExchange()中的DD_P函数来完成的.由于你手动的改变了标准控件的值,所以你需要使<BR>用SetModified()来通知ActiveX控件的属性发生改变,这样DD_P函数就会有效了.另外,在ActiveX控<BR>件的属性中的数据类型有一些是OLE_XXX类型,这些类型实际上是一些LONG型的值,并且COLECONTROL<BR>中有一些函数用来转换它们.在类型转换过程中尽量不要使用强制转换,这可能会带来一些意想不到的<BR>错误,鼓励使用缓冲区机制.<BR><BR><BR>&nbsp; 
            &nbsp; 关于控件部分其实还有很多东西,可以参阅MFC或其他的文档来了解.&nbsp; &nbsp; &nbsp; &nbsp; 
            &nbsp; &nbsp; &nbsp; &nbsp; <BR><BR><BR>3.ActiveX控件容器及实现<BR>&nbsp; 
            &nbsp; ActiveX控件的容器实际上是ActiveX控件的客户端, 
            它使用ActiveX控件提供的各种功能.但是<BR>它也同时为控件提供了一些属性和其他的特征, 使得控件可以更好的和它进行交互和操作. 
            ActiveX<BR>控件的容器实际上是一个OLE容器, 
            然后在实现了相应的接口来支持ActiveX控件后成为ActiveX控件<BR>的容器. 
            除了IUnknown外,容器程序需要用到下列接口的一部分: IOleInplaceFrame, 
            <BR>IOleInPlaceUIWindow, IOleClientSite,IOleInPlaceSite, 
            IAdviseSink, IOleControlSite, <BR>IOleControlSite, IDispatch, 
            IProperytNotifySink, IStorage, 
            IOleContainer接口的具体<BR>定义请参照MSDN.<BR><BR><BR>&nbsp; &nbsp; 
            在MFC附带的例子中有一个很好的例子, 就是VC中附带的工具ActiveX Control Test <BR>Container. 
            下面就以这个例子来解释一个ActiveX控件容器的实现及对某些问题的处理.<BR><BR>&nbsp; &nbsp; 
            <BR>&nbsp; &nbsp; 在这个例子中, 使用了VC的向导来生成一个具有Container支持的应用程序, 
            在生成的类中有一<BR>个用来包装每一个嵌入到问档中的OLE对象的类, 一般被称为xxxCntrItem, 
            在这个例子中被改名<BR>CTestContainer98Item. 创建每一个ActiveX控件时都是通过这个类来直接生成, 
            这个类维护了<BR>ActiveX控件的一些属性特征.而且这个类支持序列化, 
            这样我们就可以通过序列化来保存控件的属性<BR>状态等信息.<BR><BR><BR>&nbsp; &nbsp; 
            1).动态创建控件.<BR>&nbsp; &nbsp; 这应该是一个ActiveX控件容器最重要的任务. 为了能管理容器中的控件, 
            首先它必须能动态的<BR>创建控件. 因为每一个COM组件都具有一个唯一的ID, CLSID, ActiveX控件也不例外, 
            但是针对系统<BR>中成百上千的COM对象, 我们如何确定哪一个是ActiveX控件呢? 
            在COM基础中我们提到了为了能更快<BR>的定位COM组件并加载它, 
            COM子系统对COM组件实行了分类别管理即利用CATID来分类各种不同的COM<BR>组件, 
            ActiveX控件的CATID是CATID_Control,所以我们可以通过这个信息来找到所有在系统中注册<BR>的控件, 
            一般情况下我们是通过生成一个列表来表示所有这些控件. 
            下面是经过改写的<BR>CInsertControlDlg::RefreshControlList()函数<BR><BR><BR>CArray&lt; 
            CATID, CATID&amp; &gt; 
            m_aImplementedCategories;<BR><BR>CListBox&nbsp; &nbsp; 
            m_lbControls;<BR>ICatInformationPtr m_pCatInfo;<BR>CList&lt; CLSID, 
            CLSID&amp; &gt; m_lControls;<BR><BR>void 
            CInsertControlDlg::RefreshControlList()<BR>{<BR>BOOL 
            bDone;<BR>HRESULT hResult;<BR>IEnumGUIDPtr pEnum;<BR>ULONG 
            nImplementCategories;<BR>CATID* pcatidImpl;<BR>CLSID 
            clsid;<BR>LPOLESTR pszName;<BR>CString strName;<BR>ULONG 
            iCategory;<BR>int iItem;<BR>POSITION posControl;<BR>CString 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -