(1)Com组件是?
Com组件是以Win32动态链接库(DLL)或可执行文件(EXEs)的形式发布的可执行代码组成的。
(2)接口概念
DLL的接口就是它所输出的那些函数;C++类的接口则是该类的一个成员函数集。对于Com来说,接口是一个包含一个函数指针数组的内存结构。每一个数组元素包含的是一个由组件所实现的函数的地址。对于Com而言,接口就此内存结构。
(3)接口的作用
在Com中接口就是一切。对于客户来说,一个组件就是一个接口集。客户只能通过接口才能同com组件打交道。
用类C++的方法来实现组件
(1)接口定义
#define interface struct //接口 interface IX { virtual void Fx1() = 0; //纯虚函数 virtual void Fx2() = 0; }; interface IY { virtual void Fy1() = 0; virtual void Fy2() = 0; }; //"组件" class CA : public IX, public IY { public: virtual void Fx1() { cout << "Fx1" << endl; } virtual void Fx2() { cout << "Fx2" << endl; } virtual void Fy1() { cout << "Fy1" << endl; } virtual void Fy2() { cout << "Fy2" << endl; } }(2) 客户调用
void trace(const char* pMsg) { cout << pMsg << endl; } int main() { trace("Client: Create an instance of the component"); //创建一个组件 CA* pA = new CA; //得到一个接口 IX* pIX = pA; pIX->Fx1(); pIX->Fx2(); //使用方法 IY* pIY = pA; pIY->Fy1(); pIY->Fy2(); delete pA; return 0; }通过以上实例,可以掌握:
(1)Com接口在C++中是用纯抽象基类实现的。
(2)一个Com组件可以提供多个接口。
(3)一个c++类可以使用多继承来实现一个可以提供多个接口的组件。
(4) 组件与接口以及函数之间的关系入下图
第二部分
(1)客户同组件的交互都是通过一个接口完成的。在客户查询组件的其它接口时,也是通过接口完成的。这个接口就是IUnknown。它定义在UNKNWN.H头文件中。
interface IUnknown {d virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv) = 0; virtual ULONG __stdcall AddRef() = 0; virtual ULONG __stdcall Release() = 0; };所有的Com接口都继承了IUnknown,每个接口的vtbl中的前三个函数都是QueryInterface,AddRef,Release。这就保证了所有的接口都可以被当成IUnknown接口来处理。
(2)IUnknown指针的获取
IUnkown* CreateInstance();这个并不是最终com选择的方式。客户可以通过该函数来创建组件而不用再使用new操作符。有了IUnkown指针,我们就可以调用QueryInterface来调用其它接口函数了。
(3)QueryInterface函数
HRESULT __stdcall QueryInterface(const IID& iid,void** ppv);其中第一个参数标识客户所需的接口,现在可以理解为是一个常量。另一个指针是QueryInterface存放所请求接口指针的地址。
编写该函数必须遵守的规则:
v1: QueryInterface返回的总是同一IUnknown指针。
v2: 若客户曾经获取过某个接口,那么它将总能获取此接口。
v3: 客户可以再次获取已经拥有的接口。
v4:客户可以返回到起始接口。
v5:若能够从某个接口获取某特定接口,那么可以从任意接口都将可以获取此接口。
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if( iid == IID_IUnknown ) { *ppv = static_cast<IX*>(this); //均返回默认接口 } else if( iid == IID_IX ) { *ppv = static_cast<IX*>(this); } else if( iid == IID_IY ) { *ppv = static_cast<IY*>(this); } else { *ppv = NULL; return E_NOINTERFACE; } static_cast<IUnknown*>(*ppv)->AddRef(); //引用计数加1 }
我们说接口不会发生变化到底是什么含义呢?每一个接口都有一个唯一的接口标识符(IID)。一般情况下,我们不会改变接口,而可以建立一个新的接口并为之指定一个新的IID。当QueryInterface接收到对老IID的查询时,它将返回老的接口。而当它收到对新的IID的查询时,它将返回新的接口。所以同某个IID相应的接口将绝对不会发生变化。
(4)AddRef 和 Release
实现的是一种名为引用计数的内存管理技术,用来维护组件的生命周期。当客户从组件取得一个接口时,此引用计数将增1.当客户使用完某个接口后,组件的引用计数值减1.当引用计数为0时,组件将自己从内存中删除。当创建某个已有接口的另外一个引用时,客户也将会增大相应的组件的引用计数值。
为正确的使用引用计数,需要了解以下三条简单的规则:
v1: 在返回之前调用AddRef。对于那些返回接口指针的函数,在返回之前应用相应的指针调用AddRef。这些函数包括QueryInterface和CreateInstance。 这样,客户通过这种函数得到一个接口后,它将无需调用AddRef。
v2:使用完接口之后调用Release。在使用完某个接口之后应调用此接口的Release函数。
v3: 在赋值之后调用AddRef。
具体的规则定义如下:
V1: 输出参数规则
输出参数指的是给函数的调用者传回一个值的函数参数。在函数体中将设置此输出参数的值而不会使用调用者传进来的值。相当于输出参数为返回值。那么当这个参数为 一个接口的时候,就必须调用AddRef。
V2: 输入参数规则
输入参数指的是给函数传递某个值的参数。在函数体中将会使用这个值但是不会修改它或者将其返回给调用者。那么无需做任何操作,相当于值传递。
V3:输入-输出规则
表示一个参数同时具有输入和输出功能。那么必须在给它赋予另一个接口指针值之前调用其Release。
V4:局部变量规则
局部变量无需调用AddRef和Release。
V5:不能确定时规则
对于任何不能确定的情形,都应调用AddRef和Release对。
V6:全局变量规则
对于保存在全局变量中的接口指针,在将其传递给另外一个函数之前,必须调用其AddRef。同理,对于类成员变量的接口指针也一样。
第三部分:
(1)动态链接库
将组建放入动态链接库中,这并不是我们要将一个组件变成一个DLL。一个组件实际上并不是一个DLL, DLL只是一个组件服务器,或者说是一种发行组件的方式。 组件实际上应看成是在DLL中所实现的接口集。DLL只是一种形式。
我们现在首先来编写利用DLL来实现的Com组件。
v1: 新建一个CMPNT2的Dll工程
v2: 定义IFace.h接口文件
// // Iface.h // // Interfaces interface IX : IUnknown { virtual void __stdcall Fx() = 0 ; } ; interface IY : IUnknown { virtual void __stdcall Fy() = 0 ; } ; interface IZ : IUnknown { virtual void __stdcall Fz() = 0 ; } ; // Forward references for GUIDs extern "C" { extern const IID IID_IX ; extern const IID IID_IY ; extern const IID IID_IZ ; }v3: 定义GUID.cpp的IID申明文件
// // GUIDs.cpp - Interface IDs // #include "stdafx.h" #include <objbase.h> extern "C" { // {32bb8320-b41b-11cf-a6bb-0080c7b2d682} extern const IID IID_IX = {0x32bb8320, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8321-b41b-11cf-a6bb-0080c7b2d682} extern const IID IID_IY = {0x32bb8321, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8322-b41b-11cf-a6bb-0080c7b2d682} extern const IID IID_IZ = {0x32bb8322, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // The extern is required to allocate memory for C++ constants. }v4: 定义接口的实现文件,即组件。并在此文件中实现输出函数。
CMPNT2.cpp
// CMPNT2.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" // // Cmpnt2.cpp // To compile, use: cl /LD Cmpnt2.cpp GUIDs.cpp UUID.lib Cmpnt2.def // #include <iostream> #include <objbase.h> #include "Iface.h" using namespace std; void trace(const char* msg) { cout << "Component 2:\t" << msg << endl ;} //extern "C" _declspec(dllexport) IUnknown* CreateInstance(); // // Component // class CA : public IX, public IY { // IUnknown implementation virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ; virtual ULONG __stdcall AddRef() ; virtual ULONG __stdcall Release() ; // Interface IX implementation virtual void __stdcall Fx() { cout << "Fx wll" << endl ;} // Interface IY implementation virtual void __stdcall Fy() { cout << "Fy wll" << endl ;} public: // Constructor CA() : m_cRef(0) {} // Destructor ~CA() { trace("Destroy self.") ;} private: long m_cRef ; } ; HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { trace("Return pointer to IUnknown.") ; *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IX) { trace("Return pointer to IX.") ; *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IY) { trace("Return pointer to IY.") ; *ppv = static_cast<IY*>(this) ; } else { trace("Interface not supported."); *ppv = NULL ; return E_NOINTERFACE ; } reinterpret_cast<IUnknown*>(*ppv)->AddRef() ; return S_OK ; } ULONG __stdcall CA::AddRef() { return InterlockedIncrement(&m_cRef) ; } ULONG __stdcall CA::Release() { if (InterlockedDecrement(&m_cRef) == 0) { delete this ; return 0 ; } return m_cRef ; } // // Creation function // extern "C" IUnknown* CreateInstance() { IUnknown* pI = static_cast<IX*>(new CA) ; pI->AddRef() ; return pI ; }v5: 定义导出函数文件:
CMPNT2.def
; ; Cmpnt1 module-definition file ; LIBRARY Cmpnt1.dll DESCRIPTION '(c)1996-1997 Dale E. Rogerson' EXPORTS CreateInstance @1 PRIVATEbuild工程即可。
然后我们实现一个测试Dll组件的工程TestDllCom。
v1. 将guid.cpp 和 iface.h两个文件拷贝到该工程文件中。
v2. 创建create.h 和 create.cpp文件用于加载dll并获取导出函数。
// // Create.h // IUnknown* CallCreateInstance(char* name) ;
// // Create.cpp // #include <iostream> #include <unknwn.h> // Declare IUnknown. #include "Create.h" using namespace std; typedef IUnknown* (*CREATEFUNCPTR)() ; IUnknown* CallCreateInstance(char* name) { // Load dynamic link library into process. HINSTANCE hComponent = ::LoadLibrary(name) ; if (hComponent == NULL) { cout << "CallCreateInstance:\tError: Cannot load component." << endl ; return NULL ; } // Get address for CreateInstance function. CREATEFUNCPTR CreateInstance = (CREATEFUNCPTR)::GetProcAddress(hComponent, "CreateInstance") ; if (CreateInstance == NULL) { cout << "CallCreateInstance:\tError: " << "Cannot find CreateInstance function." << endl ; return NULL ; } return CreateInstance() ; }v3: 主要的测试Main函数
// // Client2.cpp // To compile, use: cl Client2.cpp Create.cpp GUIDs.cpp UUID.lib // #include <iostream> #include <objbase.h> #include "Iface.h" #include "Create.h" using namespace std; void trace(const char* msg) { cout << "Client 2:\t" << msg << endl ;} // // Client // int main() { HRESULT hr ; // Get the name of the component to use. char name[40] ; cout << "Enter the filename of a component to use [Cmpnt?.dll]: " ; cin >> name ; cout << endl ; // Create component by calling the CreateInstance function in the DLL. trace("Get an IUnknown pointer.") ; IUnknown* pIUnknown = CallCreateInstance(name) ; if (pIUnknown == NULL) { trace("CallCreateInstance Failed.") ; return 1 ; } trace("Get interface IX.") ; IX* pIX ; hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ; if (SUCCEEDED(hr)) { trace("Succeeded getting IX.") ; pIX->Fx() ; // Use interface IX. pIX->Release() ; } else { trace("Could not get interface IX.") ; } trace("Ask for interface IY.") ; IY* pIY ; hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ; if (SUCCEEDED(hr)) { trace("Succeeded getting IY.") ; pIY->Fy() ; // Use interface IY. pIY->Release() ; } else { trace("Could not get interface IY.") ; } trace("Release IUnknown interface.") ; pIUnknown->Release() ; return 0 ; }输出结果:
相关推荐
C#调用COM组件方法总结 本篇文章总结了C#调用COM组件的方法,涵盖了将COM类型信息转换为.NET元数据、查看元数据、测试程序等多个步骤。下面是对应的知识点: 一、将COM类型信息转换为.NET元数据 在C#调用COM组件...
该模板的来源“51pptmoban.com”暗示这是一个设计师可以找到灵感和素材的在线资源库。在这个平台上,设计师不仅能够获得免费或付费的PPT模板,还能够从其他设计师的作品中汲取灵感,进一步提升自己的设计能力。设计...
1. 拦截器是Struts2的一个重要特性,它们可以实现预处理和后处理逻辑,如日志记录、权限验证、事务管理等。常见的内置拦截器有Params(参数处理)、Validation(表单验证)、StackTrace(堆栈跟踪)等。 2. 自定义...
2. **1ppt.com的特点**: - **种类齐全**:该网站提供了多种类型的PPT模板,包括但不限于年终总结、项目报告、产品介绍等,满足不同场景的需求。 - **设计精美**:模板设计通常都非常专业,有助于提升汇报的整体...
1. **工作概述**:这部分将详细介绍你在过去一个月中所承担的任务、项目或职责。它可能包括你参与的各个项目的关键里程碑,以及你在团队中的角色。 2. **遇到的问题**:在这个部分,你可以分享你在工作中遇到的难题...
1. **封面**:通常包含医院的名称、主题(如“202X年医院年度工作总结报告”)、报告日期以及制作人或团队的信息,设计简洁大方,体现医院的专业形象。 2. **目录**:清晰列出报告的主要章节,便于读者快速定位感...
1. **项目总结的意义**:项目总结不仅仅是报告项目的成功或失败,更重要的是通过总结来学习,为未来的项目提供参考。它可以帮助团队识别出项目执行过程中的亮点和不足,以便在未来的工作中做出改进。 2. **PPT模板...
**jQuery 总结** 在实际开发中,jQuery 可以极大地提高工作效率,减少代码量,使得动态交互和界面美化变得更加简单。然而,随着 ES6 和现代前端框架的崛起,如 React 和 Vue,jQuery 在某些场景下可能不再是首选。...
标题中的“适合销售人员的年终工作总结报告的ppt模板.rar”表明这是一个专门为销售人员设计的年终总结报告的PowerPoint(PPT)模板文件,通过压缩格式(rar)进行存储。这个模板可能包含了一系列精心设计的幻灯片,...
本总结将探讨C#与Microsoft Word交互的关键知识点,帮助开发者更好地理解和掌握这一技术。 首先,C#与Word交互主要依赖于Microsoft Office Interop库,这是一个允许.NET应用程序直接与Office应用程序进行通信的接口...
《第一行代码》学习总结,已PDF的形式总结,需要的可以下载
一、Struts配置文件错误 在Struts框架中,struts-config.xml文件是核心配置文件,一旦配置错误,将导致应用程序无法正常运行。例如,在struts-config.xml文件中没有配置companyNews的action,引发javax.servlet....
该压缩包文件“简约商务蓝工作总结述职通用ppt模板.zip”包含了一个专为商务场合设计的PPT模板,适用于进行工作总结、述职报告或会议报告。这个模板的特点和知识点主要体现在以下几个方面: 1. 设计风格:模板采用...
标题中的“2014年终部门工作总结报告ppt模板.rar”是一个压缩文件,通常包含用于年度工作总结的PowerPoint演示文稿模板。这样的模板旨在帮助用户快速创建具有专业外观的报告,以便在部门或公司会议上呈现。工作总结...
1. 创建持久化类:在 com.itheima.core.po 包中创建客户持久化类和数据字典持久化类,声明了与客户数据表对应的属性并定义了各个属性的 getetter 方法。 2. 实现 DAO 层:创建客户 DAO 层接口和映射文件,以及数据...
标题中的“PPT达人学习成长感恩个人总结ppt模板.rar”表明这是一个关于个人成长与学习经验总结的PPT模板,特别适合那些热衷于PPT制作并希望通过视觉化方式表达自己成长历程的人。这个模板可能包含了从初识PPT到成为...
此模板的文件名 "51pptmoban.com" 表明它可能来源于一个在线PPT模板网站,这样的资源对于不熟悉设计或者时间紧张的用户来说非常有帮助,他们可以快速定制出专业且具有吸引力的报告。 总的来说,这个PPT模板是IT工作...
年终之际,企业和个人往往需要准备一份精美的年终总结汇报PPT,以回顾过去一年的成就、挑战和学习经验。而一份高质量的封面模板是制作这样一份汇报的基础。本文将探讨14套精心设计的免费年终总结汇报PPT封面模板,...
1. **实例化**:当服务器启动时,会加载Web应用并创建Servlet的实例。 2. **初始化**(`init()`):Servlet实例被创建后,会调用`init()`方法进行初始化工作。这个方法只执行一次。 3. **服务请求**(`service()`)...
至于“51pptmoban.com”,这可能是一个PPT模板网站的名称,阿文可能从这个网站获取了灵感或者资源,也可能是他将个人总结上传到该网站供他人参考。 综合以上信息,我们可以推测这份压缩包文件的内容可能包括以下几...