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 ;
}

   输出结果:


 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值