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

分别将组件端和客户端编译运行

运行结果





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值