第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; }
; ;compnt module-definition file ; LIBRARY cmpnt.dll DESCRIPTION 'cmpnent.dll' EXPORTS CreateInstance @1 PRIVATE
客户端
create.h
// //create.h IUnknown *CallCreateInstance(char *name);
// //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 //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
#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}} ;
运行结果