一、基础知识――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\debug\ 5。通过工程->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》的内容,希望能给大家一个入门的借鉴。
|