`

Com总结一

阅读更多

(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	PRIVATE
   build工程即可。
    然后我们实现一个测试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 ;
}

   输出结果:


 

 
  • 大小: 223.2 KB
  • 大小: 25.9 KB
分享到:
评论

相关推荐

    AWS自学总结.docx

    本人是AWS的初学者,自己参考书籍以及官网的资料,自己总结的相关资料。 我利用了假期时间,因为自己一直对网络安全和云计算安全很感兴趣,所以,就先把学习到的有关云平台安全的一些知识,从技术和管理两个方面,做...

    第一行代码总结

    《第一行代码》学习总结,已PDF的形式总结,需要的可以下载

    java中的IO操作总结(四)

    java中的IO操作总结(四) 前面已经把java io的主要操作讲完了 这一节我们来说说关于java io的其他内容 Serializable序列化 实例1:对象的序列化 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23...

    java实习总结1.docx

    java实习总结1

    Spring Security 学习总结1_3

    NULL 博文链接:https://fengshen-xia.iteye.com/blog/293799

    简洁商务风工作总结汇报PPT模板.zip

    这是一套简洁商务风工作总结汇报PPT模板,共31张; 幻灯片模板封面,使用了绿色边框搭填写工作总结汇报PPT标题和汇报年份,右侧小半边的灰色办公桌面图片进行设计。界面设计简洁商务。 PowerPoint内容页面,由29张...

    c#项目结束后的总结

    总结: 1、关于C#的知识 (1)、文件输入输出—数据流 1、FileStream类 例子: SqlDataReader reader = job.WriteFileToTxt(); FileStream jobStream = new FileStream(@fileName,FileMode.Open,FileAccess.Read...

    AJAX学习总结(一)--基于jQuery第一个实例

    NULL 博文链接:https://lc2586.iteye.com/blog/725751

    python的re模块总结.docx

    python的re模块总结全文共4页,当前为第1页。python的re模块总结全文共4页,当前为第1页。Python之re模块 python的re模块总结全文共4页,当前为第1页。 python的re模块总结全文共4页,当前为第1页。 Python用" \\\\...

    Java处理PNG透明性总结的几种方法,好用

    Java处理PNG透明性总结的几种方法,好用Java处理PNG...http://www.mocartoon.com/user/sharereturn_addMokaSharereturnInfo.do?userId=1239&recommendobjId=1196&pName=%u6D77%u87BA%u5C9B&recommendobjType=1

    WINCC 技术总结

    WINCC 技术资料,个人总结的常见问题的解决方案 唐久涛 CUMT http://hi.baidu.com/tjt999 1.wincc里的变化延迟问题 2 2.控件改变颜色延迟问题 2 3.短期归档与长期归档 2 4.C脚本播放声音 2 5.horn的使用,详见资料 2...

    蓝色商务主题年终工作总结PPT模板下载-www.officezhushou.com(1).pptx

    蓝色商务主题年终工作总结PPT模板下载-www.officezhushou.com(1).pptx

    网页弹框总结源码

    // dlg.Com("test1", "标准移动对话框", html, 480, btnClose); //两个有点类似 // dlg.Alert&#40;"系统提示", "确定要删除吗?", btn1, btn2&#41;; //dlg.Confirm("系统提示", "确定要删除吗?", btn1, btn2); /...

    Asp[1].net+面试题总结

    【京华志 www.jinghuazhi.com】Asp[1].net+面试题总结

    ppt宝藏_www.pptbz.com_总结通用模板

    我一直在用的一个用于工作总结的PPT模板,哈哈

    java中的IO操作总结(一)

    java中的IO操作总结(一) 所谓IO,也就是Input与Output的缩写。在java中,IO涉及的范围比较大,这里主要讨论针对文件内容的读写 其他知识点将放置后续章节(我想,文章太长了,谁都没耐心翻到最后) 对于文件内容...

    前端知识点总结,课程学习路线及学习链接

    前端知识点总结,课程学习路线及学习链接。部分内容如下: ## 基础知识 ### 网络知识 1. [HTTP](https://www.bilibili.com/video/BV1js411g7Fw/) 2. [DNS](https://www.bilibili.com/video/BV1GW411j7Ts/) 3. [域名...

    电力系统技术总结笔记

    本文档是一位老师讲解SSH框架做的国家电力系统的笔记。所讲解的国家电力系统中,用到的js和JQuery比较多,本人关键将的还不错。...百度云盘 : https://pan.baidu.com/s/1eR2vyE2 提取密码是:tysw

    COM+基本结构、COM+系统服务介绍、COM+应用开发

    一.COM+基本结构 5 1.Windows DNA策略 5 2.COM+基本结构 6 3.对象环境 7 二.COM+系统服务介绍 8 1.COM+队列组件 8 2.COM+事件模型 9 3.负载平衡 9 4.内存数据库(IMDB) 10 5.对其他服务的增强 10 三.COM+应用开发 11 ...

    java编码总结1

    NULL 博文链接:https://lhc1986.iteye.com/blog/1544701

Global site tag (gtag.js) - Google Analytics