第5章
本章将用DLL来实现COM组件(DLL只是组件的一种实现形式),初步实现客户和组件的完全分离。但本章其实客户与组件并没有彻底的分开,在第6和第7章将介绍更灵活的方式。
本章只是在实现组件的DLL中输出CreateInstance函数,实现组件的创建罢了。
由于组件中所有的接口函数都可以通过IUnknown接口获得,所以第3章的CreateInstance函数,需要在DLL中输出,它可以建立一个组件的实例并给客户返回一个IUnknown的接口指针。
使用DLL的原因:
一个接口(例如IX)实际上一个指向函数的指针列表(vtbl),组件为将为vtbl分配内存,并用每一个函数的地址来初始化此表格。当客户获取组件的某个接口时,它所获得的实际是指向vtbl的那块内存,因为DLL和客户的调用进程共享同一个内存空间,所以可以获得正确的vtbl所分配的内存和各个函数的地址。
其中有句话,让我印象深刻。“两个不同进程中的指针可以包含相同的地址值,但它们实际指向的是不同的物理内存”。这让我想起了Linux下使用fork创建子进程的一段代码。
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
using namespace std;
int main(void)
{
int iTest;
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
exit(1);
}
else if(pid == 0)
{
cout<<"main process iTest address: "<<std::hex<<&iTest<<endl;
sleep(3);
exit(0);
}
else
{
cout<<"fork process iTest address: "<<std::hex<<&iTest<<endl;
sleep(3);
exit(0);
}
}
运行结果
fork process iTest address: 0xbfb464cc
main process iTest address: 0xbfb464cc
可见在子进程复制了父进程的数据段,但是其实iTest是映射到不同的物理内存单元
本章的程序代码如下:
组件端
cmpnt.cpp cmpnt.def
//
//cmpnt.cpp
//use: cl /LD cmpnt.cpp cmpnt.def guids.cpp uuid.lib
//
#include <objbase.h>
#include "iface.h"
#include <iostream>
#include <string>
using namespace std;
void trace(string msg)
{
cout<<msg<<endl;
}
class CA:public IX
{
virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual void __stdcall Fx()
{
cout<<"CA:Fx"<<endl;
}
public:
CA():m_cRef(0){;}
~CA()
{
trace("Destroy self");
}
private:
long m_cRef;
};
HRESULT 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 CA::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG CA::Release()
{
if(InterlockedDecrement(&m_cRef) == 0)
{
delete this;
return 0;
}
return m_cRef;
}
extern "C" IUnknown *CreateInstance()
{
IUnknown *pIunknown = static_cast<IUnknown*>(new CA());
pIunknown->AddRef();
return pIunknown;
}
cmpnt.def
;
;compnt module-definition file
;
LIBRARY cmpnt.dll
DESCRIPTION 'cmpnent.dll'
EXPORTS CreateInstance @1 PRIVATE
客户端
create.h
//
//create.h
IUnknown *CallCreateInstance(char *name);
create.cpp
//
//create.cpp
//
#include <iostream>
#include <unknwn.h>
using namespace std;
typedef IUnknown* (*CreateFuncPtr)();
IUnknown *CallCreateInstance(char *name)
{
HINSTANCE hInstance = ::LoadLibrary(name);
if(hInstance == NULL)
{
cout<<"callcreateinstance error:can not load compnent"<<endl;
return NULL;
}
CreateFuncPtr CreateInstance = (CreateFuncPtr)::GetProcAddress(hInstance , "CreateInstance");
if(CreateInstance == NULL)
{
cout<<"callcreateinstance error:can not find createinstance func"<<endl;
return NULL;
}
return CreateInstance();
}
client.cpp
//client.cpp
//use: cl client.cpp create.cpp guids.cpp uuid.lib
//
#include <objbase.h>
#include "iface.h"
#include "create.h"
#include <iostream>
#include <string>
using namespace std;
void trace(string msg)
{
cout<<"client: "<<msg<<endl;
}
int main(void)
{
HRESULT hr;
IUnknown *pIunknown = CallCreateInstance("cmpnt.dll");
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))
{
trace("succeeded getting ix");
pIx->Fx();
pIx->Release();
}
else
{
trace("could not get ix");
}
trace("release iunknown interface");
pIunknown->Release();
return 0;
}
客户端和组件端共用文件
iface.h
#include "objbase.h"
interface IX:IUnknown
{
virtual void __stdcall Fx() = 0;
};
interface IY:IUnknown
{
virtual void __stdcall Fy() = 0;
};
interface IZ:IUnknown
{
virtual void __stdcall Fz() = 0;
};
extern const IID IID_IX
guids.cpp
#include <objbase.h>
// IIDs
// {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}} ;
分别将组件端和客户端编译运行
运行结果