com技術內幕--讀書筆記(5)

第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}} ;

分別將組件端和客戶端編譯運行

運行結果





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章