一、基础知识――DLL 的调试
方法①: 对DLL的工程DEBUG,
在DLL工程的Project Setting->Debug->Executable for debug 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/library modules中加入:../hook/debug/hook.lib
4。为了找到DLL,需要在工程设置->Debug->Working directory中加入:e:\hook\debug5。通过工程->Insert Project into Workspace将hook.dsp工程加入Test项目中。
6。设置hook工程为活动工程,在工程>Debug>Executable for Debug session中加入:
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创建一个类型为win32 dll工程
创建一个名为MathCOM的win32 dll工程。在向导的第二步选择"A smiple dll project"选项。当然如果你选择一个空的工程,那你自己定义DllMain。
1.2 增加注册功能
作为COM必须要注册与注销的功能。
增加一个MathCOM.def文件:DEF文件是模块定义文件(Module Definition File)。它允许引出符号被化名为不同的引入符号。
//MathCOM.def文件
; MathCOM.def : Declares the module parameters.
LIBRARY "MathCOM.DLL"
EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE
DllUnregisterServer这是函数名称 @4<――这是函数序号 PRIVATE
DllRegisterServer() 函数的作用是将COM服务器注册到本机上。
DllUnregisterServer() 函数的作用是将COM服务器从本机注销。
1.3 MathCOM.cpp文件
现在请将 MathCOM.cpp 文件修改成如下:
// MATHCOM.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include <objbase.h>
#include <initguid.h>
#include "MathCOM.h"
//standard self-registration table
const char * g_RegTable[][3]={
{"CLSID\\{00000000-0000-0003-0000-000000000000}",0,"MathCOM"},
{"CLSID\\{00000000-0000-0003-0000-000000000000}\\InprocServer32",
0,
(const char * )-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}"},
};
HINSTANCE g_hinstDll;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
g_hinstDll=(HINSTANCE)hModule;
return TRUE;
}
/********************************************************************
* Function Declare : DllRegisterServer
* Explain : self Registration routine
********************************************************************/
STDAPI DllRegisterServer(void)
{
HRESULT hr=S_OK;
char szFileName [MAX_PATH];
::GetModuleFileName(g_hinstDll,szFileName,MAX_PATH);
int nEntries=sizeof(g_RegTable)/sizeof(*g_RegTable);
for(int i =0;SUCCEEDED(hr)&&i<nEntries;i++)
{
const char * pszKeyName=g_RegTable[i][0];
const char * pszValueName=g_RegTable[i][1];
const char * pszValue=g_RegTable[i][2];
if(pszValue==(const char *)-1)
{
pszValue=szFileName;
}
HKEY hkey;
long err=::RegCreateKey(HKEY_CLASSES_ROOT,pszKeyName,&hkey);
if(err==ERROR_SUCCESS)
{
err=::RegSetValueEx( hkey,
pszValueName,
0,
REG_SZ,
( const BYTE*)pszValue,
( strlen(pszValue)+1 ) );
::RegCloseKey(hkey);
}
if(err!=ERROR_SUCCESS)
{
::DllUnregisterServer();
hr=E_FAIL;
}
}
return hr;
}
/********************************************************************
* Function Declare : DllUnregisterServer
* Explain : self-unregistration routine
********************************************************************/
STDAPI DllUnregisterServer(void)
{
HRESULT hr=S_OK;
char szFileName [MAX_PATH];
::GetModuleFileName(g_hinstDll,szFileName,MAX_PATH);
int nEntries=sizeof(g_RegTable)/sizeof(*g_RegTable);
for(int i =0;SUCCEEDED(hr)&&i<nEntries;i++)
{
const char * pszKeyName=g_RegTable[i][0];
long err=::RegDeleteKey(HKEY_CLASSES_ROOT,pszKeyName);
if(err!=ERROR_SUCCESS)
hr=S_FALSE;
}
return hr;
}
STDAPI DllGetClassObject(REFCLSID rclsid ,REFIID riid,void **ppv)
{
return CLASS_E_CLASSNOTAVAILABLE;
}
STDAPI DllCanUnloadNow(void)
{
return E_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)
#define DLLCOM_H
#ifdef DLLCOM_EXPORTS
#define DLLCOM_API _declspec(dllexport)
#else
#define DLLCOM_API _declspec(dllimport)
#endif
#include "unknwn.h"
//IID_IMath
//{00000000-0000-0010-0000-000000000000}
static const GUID IID_IMath={0,0,2,{0,0,0,0,0,0,0,0}};
//CLSID_CoMath
//{00000000-0000-0011-0000-000000000000}
static const CLSID CLSID_CoMath={0,0,3,{0,0,0,0,0,0,0,0}};
///////////////////////////////
//com of IMath
DECLARE_INTERFACE_(IMath,IUnknown)
{
//IUnknown method
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD(QueryInterface)(REFIID riid,LPVOID FAR* ppvObject) PURE;
//IMath method
STDMETHOD(Add) (THIS_ INT,INT,LPLONG) PURE;
STDMETHOD(Subtract)(THIS_ INT,INT,LPLONG) PURE;
};
///////////////////
////com of CoMath
class CoMath:public IUnknown
{
private:
DWORD m_dwRefCount;
public:
CoMath();
virtual ~CoMath();
//IUnknown methods
STDMETHODIMP_(DWORD) AddRef(VOID);
STDMETHODIMP_(DWORD) Release(VOID);
STDMETHODIMP QueryInterface(REFIID riid,LPVOID FAR* ppvObject);
class PersistObj:public IPersist
{
public:
CoMath* m_pParent;
STDMETHODIMP_(DWORD) AddRef(VOID);
STDMETHODIMP_(DWORD) Release(VOID);
STDMETHODIMP QueryInterface(REFIID riid,LPVOID FAR* ppvObject);
STDMETHODIMP GetClassID(LPCLSID pclsid);
}m_persistObj;
class MathObj:public IMath
{
public:
CoMath* m_pParent;
STDMETHODIMP_(DWORD) AddRef(VOID);
STDMETHODIMP_(DWORD) Release(VOID);
STDMETHODIMP QueryInterface(REFIID riid,LPVOID FAR* ppvObject);
STDMETHODIMP Add(INT,INT,LPLONG);
STDMETHODIMP Subtract(INT,INT,LPLONG);
}m_mathObj;
};
///////////////////
////class factory of CoMath
class CoMathClassFactory:public IClassFactory
{
public:
CoMathClassFactory();
~CoMathClassFactory();
STDMETHODIMP_(DWORD) AddRef(VOID);
STDMETHODIMP_(DWORD) Release(VOID);
STDMETHODIMP QueryInterface(REFIID riid,LPVOID FAR* ppv);
STDMETHODIMP CreateInstance(IUnknown* pUnkOuter,REFIID riid,VOID** ppvObject);
STDMETHODIMP LockServer(BOOL fLook);
private:
DWORD m_dwRefCount;
};
////////////////////////
////funtion
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv);
STDAPI DllRegisterServer(void);
STDAPI DllUnregisterServer(void);
STDAPI DllCanUnloadNow(void);
DLLCOM_API int ComAdd(int x,int y);// ComAdd @5 PRIVATE
#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()
{
}
DWORD CoMath::AddRef()
{
return ++m_dwRefCount;
}
DWORD CoMath::Release()
{
DWORD dwResult=--m_dwRefCount;
if(!dwResult)
delete this;
return dwResult;
}
HRESULT CoMath::QueryInterface(REFIID riid,LPVOID FAR* ppvObject)
{
if(riid==IID_IUnknown)
{
*ppvObject=(LPUNKNOWN)this;
AddRef();
return NOERROR;
}
else if(riid==IID_IPersist)
{
*ppvObject=(LPPERSIST)&m_persistObj;
AddRef();
return NOERROR;
}
else if(riid==IID_IMath)
{
*ppvObject=(IMath*)&m_mathObj;
AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
//Persist
ULONG CoMath::PersistObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CoMath::PersistObj::Release()
{
return m_pParent->Release();
}
HRESULT CoMath::PersistObj::QueryInterface(REFIID riid,LPVOID FAR* ppvObject)
{
return m_pParent->QueryInterface(riid,ppvObject);
}
//Math
ULONG CoMath::MathObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CoMath::MathObj::Release()
{
return m_pParent->Release();
}
HRESULT CoMath::MathObj::QueryInterface(REFIID riid,LPVOID FAR* ppvObject)
{
return m_pParent->QueryInterface(riid,ppvObject);
}
HRESULT CoMath::PersistObj::GetClassID(LPCLSID pclsid)
{
*pclsid=CLSID_CoMath;
return NOERROR;
}
HRESULT CoMath::MathObj::Add(INT n1,INT n2,LPLONG lpResult)
{
*lpResult=n1+n2;
return NOERROR;
}
HRESULT CoMath::MathObj::Subtract(INT n1,INT n2,LPLONG lpResult)
{
*lpResult=n1-n2;
return NOERROR;
}
///////////////////////////////
//CoMathClassFactory
static DWORD dwServerCount=0;
static CoMathClassFactory coMathCF;
CoMathClassFactory::CoMathClassFactory():m_dwRefCount(0)
{
}
CoMathClassFactory::~CoMathClassFactory()
{
}
DWORD CoMathClassFactory::AddRef()
{
return ++m_dwRefCount;
}
DWORD CoMathClassFactory::Release()
{
DWORD dwResult=--m_dwRefCount;
if(!dwResult)
delete this;
return dwResult;
}
HRESULT CoMathClassFactory::QueryInterface(REFIID riid,LPVOID FAR* ppv)
{
*ppv=0;
if(riid==IID_IClassFactory)
*ppv=LPCLASSFACTORY(this);
else if(riid==IID_IUnknown)
*ppv=LPUNKNOWN(this);
if(*ppv)
{
LPUNKNOWN(*ppv)->AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
HRESULT CoMathClassFactory::CreateInstance(IUnknown* pUnkOuter,REFIID riid,VOID** ppvObject)
{
HRESULT hr=ResultFromScode(E_OUTOFMEMORY);
CoMath* pCoMath=NULL;
pCoMath=new CoMath;
if(pCoMath){
hr=pCoMath->QueryInterface(riid,ppvObject);
if(FAILED(hr))
delete pCoMath;
}
return hr;
}
HRESULT CoMathClassFactory::LockServer(BOOL fLook)
{
if(fLook)
dwServerCount++;
else
dwServerCount--;
return NOERROR;
}
////////////////////////
////DllGetClassObject
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv)
{
if((rclsid==CLSID_CoMath)&&(riid==IID_IClassFactory||riid==IID_IUnknown))
{
return coMathCF.QueryInterface(riid,ppv);
}
return CLASS_E_CLASSNOTAVAILABLE;
}
2.3 好,到此COM设计完成
2.4 客户端
接下来我们写个客户端程序对此COM进行测试。新建一个空的名为TestMathCOM 的 win32 Console 工程,将它添加到 MathCOM workspace 中。在TestMathCOM 工程里添加一个名为 main.cpp 的文件,此文件的内容如下:
#include "stdafx.h"
int main(int argc, char* argv[])
{
CoMathClassFactory *pCF=NULL;
IMath *pIMath=NULL;
HRESULT hr=::CoInitialize(NULL);
if(hr!=S_OK) return 0;
hr=::CoGetClassObject(CLSID_CoMath,CLSCTX_ALL,NULL,IID_IClassFactory,(VOID**)&pCF);
if(SUCCEEDED(hr))
{
if(hr=pCF->CreateInstance(NULL,IID_IMath,(VOID**)&pIMath)!=NOERROR)
return 0;
long r;
if(hr=pIMath->Add(3,4,&r)!=NOERROR)
return 0;
}
else
{
if(hr==CLASS_E_CLASSNOTAVAILABLE)
printf("%x",hr);
}
printf("Hello World!\n");
return 0;
}
说明:文章参考了部分网络帖子和《深入解析MFC》的内容,希望能给大家一个入门的借鉴。