动态链接库实现COM(COM技术内幕笔记之二)

    上一篇文章里,在一个CPP文件中实现了组件IX,IY,及组件CA,以及在客户端对接口的查询,但其还不是一个COM,COM的许多特性还没有被展现出来.比如,用动态链接库实现,没有这个我就不能根据需要随意的加载和卸载组件,组件无法复用等功能。
 
    以下这篇笔记将详细的介绍如何用动态链接库去实现COM组件。
 
    首先,我们先完成我们所需的接口的创建工作,这样,COM组件就会根据其接口实现之。
    定义IFACE.H,这个头文件要被DLL及客户端EXE共享。
   
   
//In IFACE.H
#ifndef _IFACE_H
#define _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;
}


extern "C" 
{
// {A33D4226-0F56-4e34-91F3-BF4F85761101}
static const IID IID_IX = 
0xa33d42260xf560x4e340x910xf30xbf0x4f0x850x760x110x1 } };

// {41A5F090-B33A-4ae8-A1BB-EF2D0B4F8B0E}
static const IID IID_IY = 
0x41a5f0900xb33a0x4ae80xa10xbb0xef0x2d0xb0x4f0x8b0xe } };

// {65411881-4E05-4b71-9CB5-943D5E0787C4}
static const IID IID_IZ = 
0x654118810x4e050x4b710x9c0xb50x940x3d0x5e0x70x870xc4 } };
}


#endif


 可以看到,我们定义了三个接口,分别为IX,IY,IZ.
 下面我们实现组件。新建一个DLL项目,项目名为CMNPT,在VC创建过程中默认为空dll项目.
 然后,添加头文件libport.h,和源文件libport.cpp.
 在libport.h中,定义了该dll的输出函数:
 
//In libport.h
#ifndef _LIBPORT_H
#define _LIBPORT_H

#include 
"objbase.h"

#ifndef MYLIBAPI
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif

MYLIBAPI IUnknown
* CreateInstance();  //此为输出函数,以供客户端调用,客户可以根据这个函数来创建组件。

#endif

在libport.cpp中,实现了组件CA
//In libport.cpp
#include "libport.h"
#include 
<iostream.h>
#include 
"IFACE.h"

#ifndef MYLIBAPI
#define MYLIBAPI extern "C" __declspec(dllexport)
#endif

void trace(const char* msg){cout<<"Component 1:\t"<<msg<<endl;}

//
//Component
//
class CA:public IX
{
    
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 function"<<endl;}  //该组件只实现了IX的接口,对于IY,IZ接口并不支持

public:
    
//Constrator
    CA():m_cRef(0){}
    
//Destructor
    ~CA(){trace("Destory 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
    
{
        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;
}



IUnknown
* CreateInstance()
{
    IUnknown
* pI = static_cast<IX*>(new CA); //获得组件的接口指针.
    pI->AddRef();
    
return pI;
}



 到时,一个DLL定义就完成了,下面就要实现客户端对组件的调用.
 新建一项目,名为Client.在这个项目下建立Create.h与Create.cpp文件.分别如下:



 可以看到CreateInstance是调用了LoadLibaray来调用组件的DLL,然后,获得CreateInstance函数的地址,调用后得到IUnknown的指针.
 下面的步骤是在main函数中调用CallCreateInstance函数,获得IUnknown指针,查询接口,调用组件的实现...

//In client.cpp
int main()
{
    
char name[40= "CMNPT.dll";  //Can use other Component.dll
    

    
//Create component
    trace("Get and IUnknown pointer");
    IUnknown
* pIUnknown = CallCreateInstance(name);
    
if(pIUnknown==NULL)
    
{
        trace(
"CallCreateInstance Failed");
        
return 1;
    }


    trace(
"Get Interface IX.");

    IX
* pIX = NULL;
    hr 
= pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
    
    
if(SUCCEEDED(hr))
    
{
        pIX
->Fx();
        pIX
->Release();
    }

    
else
    
{
        trace(
"Could not get interface IX.");
    }

    trace(
"Relase IUnknown interface.");

    IY
* pIY = NULL;
    hr 
= pIUnknown->QueryInterface(IID_IY,(void**)&pIY);

    
if(SUCCEEDED(hr))
    
{
        pIY
->Fy();
        pIY
->Release();
    }

    
else
    
{
        trace(
"Could not get interface IY");
    }

    pIUnknown
->Release();
    
*/
    
return 0;
}



    通过以上步骤,一个用动态链接库实现的DLL就作出来了,但其还有许多瑕疵,比如需要知道组件的dll名称及dll的存放位置,需要显式的调用GetProcAddress函数以获得组件的CreateInstance函数地址。
    有没有一种方式,不需要知道组件的dll名,也不用知道该dll在磁盘的什么地方,都可以调用到这个dll,然后加载,获得接口?这个方法在下一节中会详述。
posted @ 2007-02-12 15:02  shipfi  阅读(2804)  评论(0编辑  收藏  举报