手动编写以DLL为载体的COM

本文意在启发DLL和COM入门求知者。全文皆为手工编写代码,目的是在让大家对COM的编写和内部运行过程有个清晰的了解。
一、基础知识――DLL的调试

方法①:对DLL的工程DEBUG,
在DLL工程的ProjectSetting->Debug->Executablefordebug
session中加入你的.exe的路径和名字。可以在dll中设置断点,.exe程序必须要调用dll中函数。

方法②:对调用程序DEBUG:在settings/debug中category选additional
dlls,然后将你要调试的dll加进来。这样,即使你用loadlibrary动态加载dll,也可以加断点了

集中调试方法:

1。建立dll工程hook,建立调用工程Test

2。在Test工程中需要用到hook.dll的源文件中(或stdafx.h中)加入
#include"./hook/hook.h"
这样在该源文件中使用"::"就可以索引到hook.h中所有的导出函数、变量以及类
3。在Test的工程设置->Link->Object/librarymodules中加入:../hook/debug/hook.lib

4。为了找到DLL,需要在工程设置->Debug->Workingdirectory中加入:e:/hook/debug/

5。通过工程->InsertProjectintoWorkspace将hook.dsp工程加入Test项目中。

6。设置hook工程为活动工程,在工程>Debug>ExecutableforDebugsession中加入:
e:/test/debug/test.exe

7。现在设置断点,按F5可以正常调试了

注意:当调试的DLL被映射到其他的应用程序(非TEST)进程空间并运行时,
在该DLL中设置的断点无效,当然可以通过MessageBox来查看变量,若该DLL
是MFC扩展DLL,则还可以用TRACE或afxDump来查看变量。

二、基础知识――DLL的调用

1.静态连接:copy*.dll、*.lib、*.h

2.动态连接:LoadLibrary(…);GetProcAddress();

三、正文――手工定制简单COM组件

1、从建工程到实现注册
在这一过程中我们将完成两个个步骤:创建dll的入口函数,实现注册功能
1.1创建一个类型为win32dll工程
创建一个名为MathCOM的win32dll工程。在向导的第二步选择"Asmipledll
project"选项。当然如果你选择一个空的工程,那你自己定义DllMain。
1.2增加注册功能
作为COM必须要注册与注销的功能。
增加一个MathCOM.def文件:DEF文件是模块定义文件(ModuleDefinitionFile)。它允许引出符号被化名为不同的引入符号。

//MathCOM.def文件

;MathCOM.def:Declaresthemoduleparameters.

LIBRARY"MathCOM.DLL"

EXPORTS

DllCanUnloadNow@1PRIVATE

DllGetClassObject@2PRIVATE

DllRegisterServer@3PRIVATE

DllUnregisterServer@4PRIVATE

DllUnregisterServer 这是函数名称@4<――这是函数序号PRIVATE
DllRegisterServer()函数的作用是将COM服务器注册到本机上。
DllUnregisterServer()函数的作用是将COM服务器从本机注销。
1.3MathCOM.cpp文件
现在请将MathCOM.cpp文件修改成如下:

//MATHCOM.cpp:DefinestheentrypointfortheDLLapplication.

//#include"stdafx.h"

#include<objbase.h>

#include<initguid.h>

#include"MathCOM.h"

//standardself-registrationtable

const char *g_RegTable[][3]={

{"CLSID//{00000000-0000-0003-0000-000000000000}",0,"MathCOM"},

{"CLSID//{00000000-0000-0003-0000-000000000000}//InprocServer32",0,(constchar*)-1/*表示文件名的值*/},{"CLSID//{00000000-0000-0003-0000-000000000000}//ProgID",0,"nomad.MathCOM.1"},{"nomad.MathCOM.1",0,"MathCOM"},{"nomad.MathCOM.1//CLSID",0,"{00000000-0000-0003-0000-000000000000}"},

};
HINSTANCEg_hinstDll;
BOOLAPIENTRYDllMain(HANDLEhModule,DWORDul_reason_for_call,LPVOIDlpReserved)
{
g_hinstDll=(HINSTANCE)hModule;
returnTRUE;
}
/*********************************************************************
FunctionDeclare:DllRegisterServer*Explain:selfRegistrationroutine
********************************************************************/
STDAPIDllRegisterServer(void){HRESULThr=S_OK;
charszFileName[MAX_PATH];::GetModuleFileName(g_hinstDll,szFileName,MAX_PATH);
intnEntries=sizeof(g_RegTable)/sizeof(*g_RegTable);
for(inti=0;SUCCEEDED(hr)&&i<nEntries;i++){constchar*pszKeyName=g_RegTable[i][0];
constchar*pszValueName=g_RegTable[i][1];
constchar*pszValue=g_RegTable[i][2];
if(pszValue==(constchar*)-1){pszValue=szFileName;
}
HKEYhkey;
longerr=::RegCreateKey(HKEY_CLASSES_ROOT,pszKeyName,&hkey);
if(err==ERROR_SUCCESS)
{
err=::RegSetValueEx(hkey,pszValueName,0,REG_SZ,(constBYTE*)pszValue,(strlen(pszValue)+1));
::RegCloseKey(hkey);
}
if(err!=ERROR_SUCCESS)
{
::DllUnregisterServer();
hr=E_FAIL;
}
}
returnhr;
}/*********************************************************************
FunctionDeclare:DllUnregisterServer*Explain:selfunregistrationroutine
********************************************************************/
STDAPIDllUnregisterServer(void){HRESULThr=S_OK;charszFileName[MAX_PATH];
::GetModuleFileName(g_hinstDll,szFileName,MAX_PATH);
intnEntries=sizeof(g_RegTable)/sizeof(*g_RegTable);
for(inti=0;SUCCEEDED(hr)&&i<nEntries;i++)
[i][0];longerr=::RegDeleteKey(HKEY_CLASSES_ROOT,pszKeyName);
if(err!=ERROR_SUCCESS)hr=S_FALSE;
}
returnhr;
}
STDAPIDllGetClassObject(REFCLSIDrclsid,REFIIDriid,void**ppv)
{
returnCLASS_E_CLASSNOTAVAILABLE;
}
STDAPIDllCanUnloadNow(void){
returnE_FAIL;
}
1.4好了到现在,我的所谓COM已经实现注册与注销功能。
如果在命令行或"运行"菜单下项执行如下"regsvr32
绝对路径+MathCOM.dll"就注册此COM组件。在执行完此命令后,请查看注册表项的HKEY_CLASSES_ROOT/CLSID项看看{00000000-0000-0003-0000-000000000000}这一项是否存在。如同上方法再执行一下"regsvr32
-u绝对路径+MathCOM.dll",再看看注册表,这一项就会消失。

2、实现IMath、IPersist接口和DllGetClassObject()

2.1声明IMath和IPersist接口
IMath和IPersist接口都包括在CoMath类中(参看深入解析MFC):
#if!defined(DLLCOM_H)
#defineDLLCOM_H

#ifdefDLLCOM_EXPORTS
#defineDLLCOM_API_declspec(dllexport)

#else

#defineDLLCOM_API_declspec(dllimport)

#endif

 

#include"unknwn.h"

//IID_IMath

//{00000000-0000-0010-0000-000000000000}

staticconstGUIDIID_IMath={0,0,2,{0,0,0,0,0,0,0,0}};

//CLSID_CoMath

//{00000000-0000-0011-0000-000000000000}

staticconstCLSIDCLSID_CoMath={0,0,3,{0,0,0,0,0,0,0,0}};

///

//comofIMath

DECLARE_INTERFACE_(IMath,IUnknown)

{

//IUnknownmethod

STDMETHOD_(ULONG,AddRef)(THIS)PURE;

STDMETHOD_(ULONG,Release)(THIS)PURE;

STDMETHOD(QueryInterface)(REFIIDriid,LPVOIDFAR*ppvObject)PURE;

 

//IMathmethod

STDMETHOD(Add)(THIS_INT,INT,LPLONG)PURE;

STDMETHOD(Subtract)(THIS_INT,INT,LPLONG)PURE;

};

///

comofCoMath

classCoMath:publicIUnknown

{

private:

DWORDm_dwRefCount;

public:

CoMath();

virtual~CoMath();

//IUnknownmethods

STDMETHODIMP_(DWORD)AddRef(VOID);

STDMETHODIMP_(DWORD)Release(VOID);

STDMETHODIMPQueryInterface(REFIIDriid,LPVOIDFAR*ppvObject);

 

classPersistObj:publicIPersist

{

public:

CoMath*m_pParent;

 

STDMETHODIMP_(DWORD)AddRef(VOID);

STDMETHODIMP_(DWORD)Release(VOID);

STDMETHODIMPQueryInterface(REFIIDriid,LPVOIDFAR*ppvObject);

 

STDMETHODIMPGetClassID(LPCLSIDpclsid);

}m_persistObj;

 

classMathObj:publicIMath

{

public:

CoMath*m_pParent;
STDMETHODIMP_(DWORD)AddRef(VOID);

STDMETHODIMP_(DWORD)Release(VOID);

STDMETHODIMPQueryInterface(REFIIDriid,LPVOIDFAR*ppvObject);

 

STDMETHODIMPAdd(INT,INT,LPLONG);

STDMETHODIMPSubtract(INT,INT,LPLONG);

}m_mathObj;

};

///

classfactoryofCoMath

classCoMathClassFactory:publicIClassFactory

{

public:

CoMathClassFactory();

~CoMathClassFactory();

 

STDMETHODIMP_(DWORD)AddRef(VOID);

STDMETHODIMP_(DWORD)Release(VOID);

STDMETHODIMPQueryInterface(REFIIDriid,LPVOIDFAR*ppv);

 

STDMETHODIMPCreateInstance(IUnknown*pUnkOuter,REFIIDriid,VOID**
ppvObject);

STDMETHODIMPLockServer(BOOLfLook);

private:

DWORDm_dwRefCount;

};

funtion

STDAPIDllGetClassObject(REFCLSIDrclsid,REFIIDriid,LPVOIDFAR*ppv);

STDAPIDllRegisterServer(void);

STDAPIDllUnregisterServer(void);

STDAPIDllCanUnloadNow(void);

DLLCOM_APIintComAdd(intx,inty);//ComAdd@5PRIVATE

#endif//#if!defined(DLLCOM_H)

2.2定义IMath和IPersist接口

/

CoMath

CoMath::CoMath()

{

m_dwRefCount=0;

m_persistObj.m_pParent=this;

m_mathObj.m_pParent=this;

}

CoMath::~CoMath()

{

}

DWORDCoMath::AddRef()

{

return++m_dwRefCount;

}

DWORDCoMath::Release()

{

DWORDdwResult=--m_dwRefCount;

if(!dwResult)

deletethis;

returndwResult;

}

HRESULTCoMath::QueryInterface(REFIIDriid,LPVOIDFAR*ppvObject)

{

if(riid==IID_IUnknown)

{

*ppvObject=(LPUNKNOWN)this;

AddRef();

returnNOERROR;

}

elseif(riid==IID_IPersist)

{

*ppvObject=(LPPERSIST)&m_persistObj;

AddRef();

returnNOERROR;

}

elseif(riid==IID_IMath)

{

*ppvObject=(IMath*)&m_mathObj;

AddRef();

returnNOERROR;

}

returnResultFromScode(E_NOINTERFACE);

}

//Persist

ULONGCoMath::PersistObj::AddRef()

{

returnm_pParent->AddRef();

}

ULONGCoMath::PersistObj::Release()

{

returnm_pParent->Release();

}

HRESULTCoMath::PersistObj::QueryInterface(REFIIDriid,LPVOIDFAR*ppvObject)

{

returnm_pParent->QueryInterface(riid,ppvObject);

}

//Math

ULONGCoMath::MathObj::AddRef()

{

returnm_pParent->AddRef();

}

ULONGCoMath::MathObj::Release()

{

returnm_pParent->Release();

}

HRESULTCoMath::MathObj::QueryInterface(REFIIDriid,LPVOIDFAR*ppvObject)

{

returnm_pParent->QueryInterface(riid,ppvObject);

}

HRESULTCoMath::PersistObj::GetClassID(LPCLSIDpclsid)

{

*pclsid=CLSID_CoMath;

returnNOERROR;

}

HRESULTCoMath::MathObj::Add(INTn1,INTn2,LPLONGlpResult)

{

*lpResult=n1+n2;

returnNOERROR;

}

HRESULTCoMath::MathObj::Subtract(INTn1,INTn2,LPLONGlpResult)

{

*lpResult=n1-n2;

returnNOERROR;

}

///

//CoMathClassFactory

staticDWORDdwServerCount=0;

staticCoMathClassFactorycoMathCF;

 

CoMathClassFactory::CoMathClassFactory():m_dwRefCount(0)

{

}

CoMathClassFactory::~CoMathClassFactory()

{

}

DWORDCoMathClassFactory::AddRef()

{

return++m_dwRefCount;

}

DWORDCoMathClassFactory::Release()

{

DWORDdwResult=--m_dwRefCount;

if(!dwResult)

deletethis;

returndwResult;

}

HRESULTCoMathClassFactory::QueryInterface(REFIIDriid,LPVOIDFAR*ppv)

{

*ppv=0;

 

if(riid==IID_IClassFactory)

*ppv=LPCLASSFACTORY(this);

elseif(riid==IID_IUnknown)

*ppv=LPUNKNOWN(this);

 

if(*ppv)

{

LPUNKNOWN(*ppv)->AddRef();

returnNOERROR;

}

 

returnResultFromScode(E_NOINTERFACE);

}

 

HRESULTCoMathClassFactory::CreateInstance(IUnknown*pUnkOuter,REFIID
riid,VOID**ppvObject)

{

HRESULThr=ResultFromScode(E_OUTOFMEMORY);

CoMath*pCoMath=NULL;

pCoMath=newCoMath;

if(pCoMath){

hr=pCoMath->QueryInterface(riid,ppvObject);

if(FAILED(hr))

deletepCoMath;

}

returnhr;

}

HRESULTCoMathClassFactory::LockServer(BOOLfLook)

{

if(fLook)

dwServerCount++;

else

dwServerCount--;

returnNOERROR;

}

DllGetClassObject

STDAPIDllGetClassObject(REFCLSIDrclsid,REFIIDriid,LPVOIDFAR*ppv)

{


if((rclsid==CLSID_CoMath)&&(riid==IID_IClassFactory||riid==IID_IUnknown))

{

 

returncoMathCF.QueryInterface(riid,ppv);

}

returnCLASS_E_CLASSNOTAVAILABLE;

}

2.3好,到此COM设计完成
2.4客户端

接下来我们写个客户端程序对此COM进行测试。新建一个空的名为TestMathCOM的win32Console工程,将它添加到MathCOM
workspace中。在TestMathCOM工程里添加一个名为main.cpp的文件,此文件的内容如下:

#include"stdafx.h"

intmain(intargc,char*argv[])

{

CoMathClassFactory*pCF=NULL;

IMath*pIMath=NULL;

HRESULThr=::CoInitialize(NULL);

 

if(hr!=S_OK)return0;


hr=::CoGetClassObject(CLSID_CoMath,CLSCTX_ALL,NULL,IID_IClassFactory,(VOID**)&pCF);


if(SUCCEEDED(hr))

{

if(hr=pCF->CreateInstance(NULL,IID_IMath,(VOID**)&pIMath)!=NOERROR)

return0;

longr;

if(hr=pIMath->Add(3,4,&r)!=NOERROR)

return0;

}

else

{

if(hr==CLASS_E_CLASSNOTAVAILABLE)

printf("%x",hr);

}

 

printf("HelloWorld!/n");

return0;

}

 

说明:文章参考了部分网络帖子和《深入解析MFC》的内容,希望能给大家一个入门的借鉴。

 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值