第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}} ;
分別將組件端和客戶端編譯運行
運行結果