动态链接库实现COM(COM技术内幕笔记之二)
上一篇文章里,在一个CPP文件中实现了组件IX,IY,及组件CA,以及在客户端对接口的查询,但其还不是一个COM,COM的许多特性还没有被展现出来.比如,用动态链接库实现,没有这个我就不能根据需要随意的加载和卸载组件,组件无法复用等功能。
以下这篇笔记将详细的介绍如何用动态链接库去实现COM组件。
首先,我们先完成我们所需的接口的创建工作,这样,COM组件就会根据其接口实现之。
定义IFACE.H,这个头文件要被DLL及客户端EXE共享。
可以看到,我们定义了三个接口,分别为IX,IY,IZ.
下面我们实现组件。新建一个DLL项目,项目名为CMNPT,在VC创建过程中默认为空dll项目.
然后,添加头文件libport.h,和源文件libport.cpp.
在libport.h中,定义了该dll的输出函数:
到时,一个DLL定义就完成了,下面就要实现客户端对组件的调用.
新建一项目,名为Client.在这个项目下建立Create.h与Create.cpp文件.分别如下:
可以看到CreateInstance是调用了LoadLibaray来调用组件的DLL,然后,获得CreateInstance函数的地址,调用后得到IUnknown的指针.
下面的步骤是在main函数中调用CallCreateInstance函数,获得IUnknown指针,查询接口,调用组件的实现...
通过以上步骤,一个用动态链接库实现的DLL就作出来了,但其还有许多瑕疵,比如需要知道组件的dll名称及dll的存放位置,需要显式的调用GetProcAddress函数以获得组件的CreateInstance函数地址。
有没有一种方式,不需要知道组件的dll名,也不用知道该dll在磁盘的什么地方,都可以调用到这个dll,然后加载,获得接口?这个方法在下一节中会详述。
以下这篇笔记将详细的介绍如何用动态链接库去实现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 =
{ 0xa33d4226, 0xf56, 0x4e34, { 0x91, 0xf3, 0xbf, 0x4f, 0x85, 0x76, 0x11, 0x1 } };
// {41A5F090-B33A-4ae8-A1BB-EF2D0B4F8B0E}
static const IID IID_IY =
{ 0x41a5f090, 0xb33a, 0x4ae8, { 0xa1, 0xbb, 0xef, 0x2d, 0xb, 0x4f, 0x8b, 0xe } };
// {65411881-4E05-4b71-9CB5-943D5E0787C4}
static const IID IID_IZ =
{ 0x65411881, 0x4e05, 0x4b71, { 0x9c, 0xb5, 0x94, 0x3d, 0x5e, 0x7, 0x87, 0xc4 } };
}
#endif
#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 =
{ 0xa33d4226, 0xf56, 0x4e34, { 0x91, 0xf3, 0xbf, 0x4f, 0x85, 0x76, 0x11, 0x1 } };
// {41A5F090-B33A-4ae8-A1BB-EF2D0B4F8B0E}
static const IID IID_IY =
{ 0x41a5f090, 0xb33a, 0x4ae8, { 0xa1, 0xbb, 0xef, 0x2d, 0xb, 0x4f, 0x8b, 0xe } };
// {65411881-4E05-4b71-9CB5-943D5E0787C4}
static const IID IID_IZ =
{ 0x65411881, 0x4e05, 0x4b71, { 0x9c, 0xb5, 0x94, 0x3d, 0x5e, 0x7, 0x87, 0xc4 } };
}
#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;
}
#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;
}
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,然后加载,获得接口?这个方法在下一节中会详述。