简单接口实现规范.doc_第1页
简单接口实现规范.doc_第2页
简单接口实现规范.doc_第3页
简单接口实现规范.doc_第4页
简单接口实现规范.doc_第5页
已阅读5页,还剩63页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

简单接口实现规范作者:Softit增补:小小企鹅,StoneLee最新更新:2003-5-27预备知识:l C+的基础概念,特别是虚函数和多态l COM,建议参考书籍COM 本质论(ISBN:7-5083-0611-2)第一章 整体概念第一节 概要说明基于组件的软件设计方法是软件工业实践的一个基本成功经验,在软件设计过程中要考虑模块的少耦合少依赖,这是模块重用的基础。C+虚函数为接口提供了理论基础。之所以称之为“简单接口”,是相对于COM和CORBA组件而言,大部分小组件不需要支持引用计数、多语言开发、跨网络运行等特性。运用简单接口还可以很容易写出模块化的插件,例如,可以将棋牌类客户端做成插件形式,但外观可以使用公用的界面框架,也可以嵌入到游戏大厅里。简单接口实现的组件将来改造成ActiveX组件也很容易。第二节 名词解释一、 图示二、 说明1、 接口 一组纯虚函数的集合。实现时,是个头文件,里面全部是纯虚函数,从C+观点讲,就是一个函数指针表(vfnTable),详细可参考COM有关书籍。例如,上图中的IFoo部分。2、 服务实现接口的组件,供客户应用程序调用,我们称此组件提供了一个支持接口的服务,或简单理解成Server也可以。服务一般以DLL或lib库和接口的头文件一起提供。(当然:最好还应该有一个说明文档)。例如,上图中的CFoo部分。3、 客户使用接口的程序,一般是调用接口的具体应用程序,也可理解为Client。一般客户都是独立成为一个应用程序。如上图所示,为CExtern部分。4、 回调接口有的时候,客户通过接口调用服务的相关方法后,需要知道这些方法是否执行成功。但是存在下面两种可能:1) 由于服务可能是异步模式,所以客户并不能马上通过方法的返回值获得。2) 或则,为了程序的结构清晰,服务并不想通过接口的调用的返回值,而是希望通过调用客户的一些固定的函数来通知客户事件发生。这时,就需要用到回调接口。和接口不同,回调接口实际是服务发起的,由客户实现的。而接口却是有客户发起的,由服务实现的。接口的使用,是为了把定义与实现分离,这样能提高程序各模块之间的独立性。类似于COM组件的连接点。“服务”有些事件要通知“客户”,通过回掉接口实现。回调接口也是用头文件实现,里面是纯虚函数。客户必须继承和实现这个回调接口。然后将实现回调接口的对象的指针传给服务(一般在服务创建函数中),这样服务就可以在一些约定好的事件中回调客户的程序代码。如上图所示,CFoo通过回调接口(IFooSink)调用外部模块CExtern的函数,CExtern实现IFooSink。CExtern实例化后,将实例后的指针传给CFoo。一般定义为以Sink为结束的接口,如IFooSink,表明此接口是供IFoo回调的。旁注:Sink的英文意思是“接收器”。5、 多接口和多回调接口I、 多接口任何一个客户,都可能用到多个服务。比如:我们有一个自动下载的客户程序,CAutoDownLoad,它要使用以下接口为其服务,包括通信接口ICommunicate、资源接口IResMgr。这样,我们在CAutoDownLoad里,只有获得ICommunicate和IResMgr两个指针,就可以通过其实例(即服务对象)实现相关的接口功能。II、 多回调接口同时,一个客户,也可能实现多个回调接口。比如:一个游戏服务器CGameServer,被多个回调接口触发,比如:通信回调接口ICmmSink,数据库完成通知IDBSink。这时,CGameServer只要简单地继承这几个回调接口,如下:Class CGameServer:public ICmmSink,IDBSink 然后,CGameServer将自己的实例化后的指针,分别传给对应的通信和数据库服务,让他们回调自己。第三节 模块之间通信方式比较:基于接口和基于类共享的方法的比较简单接口间当一个模块IFoo需要回调其它模块(IExtern)的函数时,在IExtern模块中实现回调接口(IFooSink,IExtern继承接口IFooSink即可),将IFooSink传送给IFoo,这种方式是两个接口间交流的方式,约束简明。接口方式调用的另一个极重要的好处是多态性,即一个接口可以有多种不同的实现方法,如一个CArchieve所包含的月报表数据,通过显示接口IView,可以对应三种不同的显示实现方式(柱状、饼状、数字),而且用户可以在运行时刻动态选择其中的一种或几种显示方式。相反,如果要调用C+类CFoo的一个方法,需包含CFoo的头定义,头文件中可能有大量私有的,与类之间通讯无关的东西,如CFoo可能包含了很多其它头文件、很多自定义宏等。CFoo不知有哪些个方法被别人调用、有哪些public属性被引用,因此不能轻易修改自已函数,则将两个类的实现绑定在一起,因无法知道一个类调用了另一个类的哪些方法和引用了哪一些public成员,因而类的定义不能轻易修改,否则可能会影响很多模块。第二章 接口的具体实现第一节 接口定义样板文件:IFoo.hclass IFoo virtual void Release() = NULL;/ 释放对象,见下面的说明“接口对象的销毁”virtual BOOL Add(LPCSTR szName, DWORD dwReserved=0) = NULL;virtual BOOL Delete(LPCSTR szName) = NULL;接口是一经发布尽可能少修改,所以一些将来可能会修改或一些重要的虚函数定义时要加一个dwReserved参数,便于将来扩充第二节 接口对象的创建:即服务的实例化一、 方法接口对象的创建即new一个实现此接口的对象,将此对象的接口指针返回即可。创建将来可能更改接口实现时,需要支持版本控制,如互联网应用程序一般要求后期发布的程序与早期发布的模块接口兼容。二、 范例一般我们将接口实现放在一个DLL中,然后,通过DLL的输出函数来实例化接口对象。如下面所示意:1、 DLL实例化一般写在IFoo.cpp中extern C _cdecl _declspec(dllexport) BOOL CreateFooObject(/*out*/IFoo* ppFoo)if(ppFoo = NULL)/ 先判断传入指针是否为空,是编码的好习惯return FALSE;CFoo *pFoo = new CFoo();/ 这里实例化服务CFooif(pFoo = NULL)/ 好习惯return FALSE;/ 注意:通过类型转换,将CFoo转为IFoo返回给客户*ppFoo = static_cast(pFoo);return TRUE;2、 服务的实现:Server.dspI、 Foo.hclass CFoo: public IFoo private:CNameList m_listName;/ 假设CNameList类是一个动态数组,/ 支持Insert、RemoveCurrent、Top、GetCurrent、Next等方法public:virtual void Release();virtual BOOL AddName(LPCSTR szName, DWORD dwReserved);virtual BOOL DeleteName(LPCSTR szName);II、 Foo.cpp#include “IFoo.h”#include “Foo.h”BOOL CFoo:AddName(LPCSTR szName, DWORD dwReserved)return(m_listName.Insert(szName);BOOL CFoo:DeleteName(LPCSTR szName)m_listName.Top();BOOL bFind = FALSE;char *szListName;while(szListName = m_listName.GetCurrent()if (strcmp(szListName, szName) = 0)bFind = TRUE;m_listName.RemoveCurrent();elsem_listName.Next();return bFind;void CFoo:Release()m_listName.Top();while(m_listName.GetCurrent() m_listName.RemoveCurrent();delete this;3、 客户的使用:Client.dspI、 Extern.hclass CExternprivate:IFoo*m_pFoo;public:CExtern() m_pFoo = NULL;void CreateIFoo();void UseIFoo();void NotUserIFooNever();II、 Extern.cpp#include “./Include/IFoo.h”/ 后面有一章“一些规范”里会解释这个路径#include “Extern.h”void CExtern:CreateIFoo()。/ 为阅读,省略了一些加栽dll的代码if (!CreateFoolObject(&m_pFoo);m_pFoo = NULL;void CExtern:UseIFoo()if (m_pFoo)m_pFoo-AddName(“Test”);void CExtern:NotUseIFooNever()if (m_pFoo)m_pFoo-Release();m_pFoo = NULL;三、 先实例化服务,再实例化客户有时,先实例化服务,然后再实例化客户。例如:我们的游戏服务器和游戏框架,就是采用这种方式。此时,由服务调用客户的创建函数,并将自己的接口指针做为参数传给客户。第三节 接口对象的销毁接口对象的销毁,不应该由客户用delete来完成。一般,接口对象都提供一个Release(或close)虚函数,自己负责销毁自己。这个可以从上面例子可以看出,如果用下面的下法:void CExtern:NotUseIFooNever()if (m_pFoo)delete m_pFoo;m_pFoo = NULL;那么,对于CExtern,它只看到了IFoo这片内存区,是不知道有m_listName这样一个动态列表的对象的,所以当调用delete m_pFoo时,m_listName是无法删除的,这样只能产生内存泄露。所以,必须而且只能由对象自己完成销毁工作,就如上例中实现的NotUserIFooNever()中调用的一样。同时,还有一个范例说明,销毁应该由对象自己完成:即DLL的运行期库问题。参看:Windows核心编程(ISBN:7-111-07945-0)P465和P131。下面有一个简单的程序,大家可以调试以下:l 包括不调用Release()函数l 去掉Release()里的delete thisl 将main()里的pInterface-Release()注释,换成下面的delete pInterface看一下内存和CDestruction()的调用情况,可以较详细地了解简单接口。/ TestInterfaceDestruction.cpp : Defines the entry point for the console application./#include stdafx.h#include #include #define new DEBUG_NEW/ 为了检测内存泄露,只能使用DEBUG_NEWclass IDestructionpublic:virtual void Release() = 0;virtual void Create() = 0;virtual void Cal() = 0;class CDestruction : public IDestructionpublic:CDestruction()m_piA = 0;m_piB = 0;CDestruction()int c = 1;public:virtual void Release()if (m_piA) delete m_piA;m_piA = 0;if (m_piB) delete m_piB;m_piB = 0;delete this;virtual void Create()m_piA = new(int);assert(m_piA);m_piB = new(int);assert(m_piB);virtual void Cal()assert(m_piA);assert(m_piB);int c;c = *m_piA + *m_piB;private:int*m_piA, *m_piB;void LibCreate(IDestruction* ppInterface)CDestruction *pCDes;pCDes = new CDestruction();assert(pCDes);pCDes-Create();*ppInterface = (IDestruction*) pCDes;int main(int argc, char* argv)IDestruction *pInterface;LibCreate(&pInterface);pInterface-Release();/ delete pInterface;return 0;第四节 关于_stdcall和_cdecl一、 说明接口是基于虚拟函数的,所以对于函数的理解必须保证大家一致,这主要关系到下面几个问题。n 函数的参数的传入次序n 函数的参数的压栈出栈管理n 变长参数的使用在VC环境里,主要有两类调用模式,即_stdcall和_cdecl,对这两种调用模式的区别,详细看参看MSDN之URL:mk:MSITStore:C:Program%20FilesMicrosoft%20Visual%20StudioMSDN2001JUL1033vccore.chm:/html/_core_calling_conventions.3a_.overview.htm二、 显式地说明接口的调用方式所以为了避免对接口理解的不一致性,需要显式地说明接口的调用方式。例如:class IFoo void _cdecl DeleteName();VC缺省情况下,使用_cdecl。而在COM中,我们一般发现定义接口函数如下:STDMETHOD(Start)();其实:STDMETHOD定义如下:#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method而STDMETHODCALLTYPE定义如下:#ifdef _68K_#define STDMETHODCALLTYPE _cdecl#else#define STDMETHODCALLTYPE _stdcall#endif这说明,COM为了保证接口的无歧义性,显式地定义了其接口函数。因此,我们的简单接口规范也要求这样做。有两类函数需要使用这个定义:l 简单接口的虚表函数(即接口)l DLL的输出函数或Lib库的全局函数第五节 辅助类Helper和Factory:帮您创建接口一、 接口的辅助类:helper类1、 概念一般一个接口对应一个helper类, 帮助创建和维护一个接口对象,免除加载、释放dll、查找和执行输出函数、版本管理之苦,只要申明一个CIFooHelper变量并执行CIFooHelper.Create()即可轻松获取接口对象。helper类一般提供两个方法Create()和Close()来管理接口对象。CIFooHelper.Create()或LibCreate()创建接口对象步骤如下:l 如果是Create()创建:1、 通过LoadLibrary()查找并加载接口所在的dll2、 在dll中查找接口的创建函数3、 调用接口的创建函数,获得接口对象指针4、 okl 如果是LibCreate()创建:CIFooHelper.Close()销毁接口对象如下:1、 直接调用接口对象的Release()函数2、 使用helper类的几个要点l 一个helper类对象,存放一个接口指针l 一个helper类实例,对应一个接口服务实例,即它们是一一对应和完全绑定的l 接口服务实例,通过helper类的Create()或LibCreate()函数增加l 接口服务实例,通过helper类的Close()函数销毁l helper类重载-指针,这样,可以通过引用helper类来引用接口(但前提,必须先Create()成功)3、 helper类一般在接口头文件里实现由于helper类是服务接口编写人编写,同时提供给客户项目编写人,放入客户的工程中。所以,从简单考虑,最好将helper类的声明和实现,全部放在接口的头文件里。这样,只需要提供一个头文件给客户工程即可。越简单,越不容易犯错误,不是这样的吗?4、 Helper类范例I、 CIFooHelper(位于:IFoo.h)class CIFooHelper typedef BOOL (_cdecl *ProcCreateFooObject)(/*output*/IFoo* ppFoo);public:CIFooHelper() m_hDll = NULL; m_pIFoo = NULL;m_szErrMsg0 = 0;CIFooHelper() Close();if (m_hDll)/ 注意:如果是lib方式,由于m_hDll总是NULL,所以下面的:;FreeLibary()不会被调用:FreeLibrary(m_hDll);m_hDll = NULL;BOOLCreate()/ 创建接口对象try if (m_hDll = NULL)/ 第一次调用时未加载dll,下面进行加载dll#ifdef _DEBUGm_hDll = :LoadLibrary(FooD.dll);if (m_hDll = NULL)throw Cant load FooD.dll;#elsem_hDll = :LoadLibrary(Foo.dll);if (m_hDll = NULL)throw Cant load Foo.dll;#endif/ 下面获得dll的输出函数,即接口对象的创建函数ProcCreateFooObject proc = NULL;proc = (ProcCreateFooObject):GetProcAddress(m_hDll, CreateFooObject);if (proc = NULL)throw Cant GetProcAddress(CretaeFooObject);/ 下面调用dll的输出函数,来创建接口对象if (!proc(&m_pFoo)throw CreateFooObject() error!;return TRUE;/ 到此,接口对象创建成功!catch(LPCSTR szMsg)lstrcpyn(m_szErrMsg, szMsg, sizeof(m_szErrMsg);return FALSE;catch(.)lstrcpyn(m_szErrMsg, Unknown Error!, sizeof(m_szErrMsg);return FALSE;voidClose();/ 销毁接口对象if (m_pFoo)m_pFoo-Release();m_pFoo = NULL;m_bValid = FALSE;/ 重载-,将helper对象的引用直接转为接口的引用IFoo* operator -()return m_pIFoo;/ 由于Helper通过重载-实现接口的调用,所以有必要提供一个判断接口指针是否有效的函数BOOL IsValid()return (m_pIFoo != NULL);char*GetErrMsg() return m_szErrMsg;/ 返回错误信息;private:IFoo*m_pIFoo;/ 如果一个helper只对应一个接口对象HINSTANCEm_hDll;/ 动态连接库的加载与否标志charm_szErrMsg128;/ 错误信息II、 Extern.hclass CExtern public:CExtern();CExtern();BOOL CreateIFoo();void UseIFoo();void CloseIFoo();private:CIFooHelperm_IFooHelper;III、 Extern.cppCExtern:CExtern()CExtern:CExtern()CloseIFoo();BOOL CExtern:CreateIFoo()if (!m_IFooHelper.Create()TRACE(“%s”, m_IFooHelper.GetErrMsg():elsereturn TRUE;void CExtern:UseIFoo()if (m_IFooHelper.IsValid()m_IFooHelper-DeleteName(“abc);void CExtern:CloseIFoo()m_IFooHelper.Close();5、 Helper类的优点和缺点I、 Helper类的优点l Create()简化了初始化操作l 包含和隐藏了接口,非常简洁II、 Helper类的优点l 一个Helper类对象实例只能对应一个接口对象实例l 容易疏忽Create()和IsValid()函数,特别是IsValid()函数,(下面可以参考Factory,由于直接使用接口指针,所以犯错误的机会相对要小一点)二、 接口的辅助类:Factory类1、 概念上面提到的Helper类非常好用,但有其缺陷。如果我们希望有这样一种helper类,可以管理多个接口对象,甚至还可以管理一个包(例如:某个dll文件)的多个类型的多个接口对象。(不过,我个人的习惯,宁愿写多个Factory类,这样结构更清晰)这时,我们就要用到Factory类。2、 使用Factory类的几个要点l Factory类不包含接口的指针,由客户(即CExtern)自己管理接口对象l Factory类只管接口对象的创建,但不管其销毁,即Factory类只有Create()函数,但没有Close()函数l 接口对象的销毁,直接调用接口的Release()方法l 一般情况下,要求Factory的生命期长于接口对象,即销毁Factory对象前,必须先销毁由其创建的接口对象l 接口的使用,直接使用接口指针,不通过Factory类3、 Factory类一般在接口头文件里实现,可以和Helper类在一起由于Factory类是服务接口编写人编写,同时提供给客户项目编写人,放入客户的工程中。所以,从简单考虑,最好将Factory类的声明和实现,全部放在接口的头文件里。这样,只需要提供一个头文件给客户工程即可。同时,我们可以在接口文件里即包含helper类,也可以包含Factory类。4、 Factory类范例I、 CIFooFactory(位于:IFoo.h)class CIFooFactory typedef BOOL (_cdecl *ProcCreateFooObject)(/*output*/IFoo* ppFoo);public:CIFooFactory() m_hDll = NULL; m_szErrMsg0 = 0;CIFooFactory() if (m_hDll):FreeLibrary(m_hDll);m_hDll = NULL;BOOLCreate(IFoo* ppFoo)/ 创建接口对象try if (m_hDll = NULL)/ 第一次调用时未加载dll,下面进行加载dll#ifdef _DEBUGm_hDll = :LoadLibrary(FooD.dll);if (m_hDll = NULL)throw Cant load FooD.dll;#elsem_hDll = :LoadLibrary(Foo.dll);if (m_hDll = NULL)throw Cant load Foo.dll;#endif/ 下面获得dll的输出函数,即接口对象的创建函数ProcCreateFooObject proc = NULL;proc = (ProcCreateFooObject):GetProcAddress(m_hDll, CreateFooObject);if (proc = NULL)throw Cant GetProcAddress(CretaeFooObject);/ 下面调用dll的输出函数,来创建接口对象if (!proc(ppFoo)throw CreateFooObject() error!;return TRUE;/ 到此,接口对象创建成功!catch(LPCSTR szMsg)lstrcpyn(m_szErrMsg, szMsg, sizeof(m_szErrMsg);return FALSE;catch(.)lstrcpyn(m_szErrMsg, Unknown Error!, sizeof(m_szErrMsg);return FALSE;char*GetErrMsg() return m_szErrMsg;/ 返回错误信息private:HINSTANCEm_hDll;/ 动态连接库的加载与否标志charm_szErrMsg128;/ 错误信息II、 Extern.hclass CExtern public:CExtern();CExtern();BOOL CreateIFoo();void UseIFoo();void CloseIFoo();private:IFoo*m_pFoo1, *m_pFoo2;CIFooFactorym_IFooFactory;III、 Extern.cppCExtern:CExtern()m_pFoo1 = m_pFoo2 = NULL;CExtern:CExtern()/ 注意:一定要调用这个函数,由于是先调用类的的析构函数,/ 再调用成员变量的析构函数,所以,这样处理即安全,又不存在任何问题/ 否则,由可能先析构m_IFooFactory,这样dll就不存在了,/ 再析构或使用接口对象,就会出现错误/ 详细可参考C+编程思想第261页,ISBN:7-111-07116-6CloseIFoo();BOOL CExtern:CreateIFoo()/ 创建接口对象要用到Factory类if (!m_IFooFactory-Create(&m_pFoo1)TRACE(“%s”, m_IFooFactory.GetErrMsg();return FALSE;if (!m_IfooFactory-Create(&m_pFoo2)if (m_pIFoo1)m_pIFoo1-Release();return FALSE;void CExtern:UseIFoo()if (m_pFoo1)m_pFoo1-DeleteName(“abc);if (m_pFoo2)m_pFoo2-AddName(“efg”);void CExtern:CloseIFoo()if (m_pFoo1)m_pFoo1-Release();if (m_pFoo2)m_pFoo2-Release();三、 一个包文件输出多个接口对象的辅助类做法用多个helper类或多个Factory类解决问题,1、 范例例如:包FGHoo.dll(或FGHooD.dll)输出三个接口对象,分别是IFoo、IGoo、IHoo,则我们的接口编写如下:下面代码位于IFGH.h里面class CIFooFactory typedef BOOL (_cdecl *ProcCreateFooObject)(/*output*/IFoo* ppFoo);public:CIFooFactory() m_hDll = NULL;m_szErrMsg0 = 0;CIFooFactory() if (m_hDll):FreeLibrary(m_hDll);m_hDll = NULL;BOOLCreate(IFoo* ppFoo)/ 创建接口对象try if (m_hDll = NULL)/ 第一次调用时未加载dll,下面进行加载dll#ifdef _DEBUGm_hDll = :LoadLibrary(FGHooD.dll);if (m_hDll = NULL)throw Cant load FGHooD.dll;#elsem_hDll = :LoadLibrary(FGHoo.dll);if (m_hDll = NULL)throw Cant load FGHoo.dll;#endif/ 下面获得dll的输出函数,即接口对象的创建函数ProcCreateFooObject proc = NULL;proc = (ProcCreateFooObject):GetProcAddress(m_hDll, CreateFooObject);if (proc = NULL)throw Cant GetProcAddress(CretaeFooObject);/ 下面调用dll的输出函数,来创建接口对象if (!proc(ppFoo)throw CreateFooObject() error!;return TRUE;/ 到此,接口对象创建成功!catch(LPCSTR szMsg)lstrcpyn(m_szErrMsg, szMsg, sizeof(m_szErrMsg);return FALSE;catch(.)lstrcpyn(m_szErrMsg, Unknown Error!, sizeof(m_szErrMsg);return FALSE;char*GetErrMsg() return m_szErrMsg;/ 返回错误信息private:HINSTANCEm_hDll;/ 动态连接库的加载与否标志charm_szErrMsg128;/ 错误信息class CIGooFactory typedef BOOL (*ProcCreateGooObject)(/*output*/IGoo* ppGoo);public:CIGooFactory() m_hDll = NULL;m_szErrMsg0 = 0;CIGooFactory() if (m_hDll):FreeLibrary(m_hDll);m_hDll = NULL;BOOLCreate(IGoo* ppGoo)/ 创建接口对象try if (m_hDll = NULL)/ 第一次调用时未加载dll,下面进行加载dll#ifdef _DEBUGm_hDll = :LoadLibrary(FGHooD.dll);if (m_hDll = NULL)throw Cant load FGHooD.dll;#elsem_hDll = :LoadLibrary(FGHoo.dll);if (m_hDll = NULL)throw Cant load FGHoo.dll;#endif/ 下面获得dll的输出函数,即接口对象的创建函数ProcCreateGooObject proc = NULL;proc = (ProcCreateGooObject):GetProcAddress(m_hDll, CreateGooObject);if (proc = NULL)throw Cant GetProcAddress(CretaeGooObject);/ 下面调用dll的输出函数,来创建接口对象if (!proc(ppFoo)throw CreateGooObject() error!;return TRUE;/ 到此,接口对象创建成功!catch(LPCSTR szMsg)lstrcpyn(m_szErrMsg, szMsg, sizeof(m_szErrMsg);return FALSE;catch(.)lstrcpyn(m_szErrMsg, Unknown Error!, sizeof(m_szErrMsg);return FALSE;char*GetErrMsg() return m_szErrMsg;/ 返回错误信息private:HINSTANCEm_hDll;/ 动态连接库的加载与否标志charm_szErrMsg128;/ 错误信息class CIHooFactory typedef BOOL (_cdecl *ProcCreateHooObject)(/*output*/IHoo* ppHoo);public:CIHooFactory() m_hDll = NULL;m_szErrMsg0 = 0;CIHooFactory() if (m_hDll):FreeLibrary(m_hDll);m_hDll = NULL;BOOLCreate(IHoo* ppHoo)/ 创建接口对象try if (m_hDll = NULL)/ 第一次调用时未加载dll,下面进行加载dll#ifdef _DEBUGm_hDll = :LoadLibrary(FGHooD.dll);if (m_hDll = NULL)throw Cant load FGHooD.dll;#elsem_hDll = :LoadLibrary(FGHoo.dll);if (m_hDll = NULL)throw Cant load FGHoo.dll;#endif/ 下面获得dll的输出函数,即接口对象的创建函数ProcCreateHooObject proc = NULL;proc = (ProcCreateHooObject):GetProcAddress(m_hDll, CreateHooObject);if (proc = NULL)throw Cant GetProcAddress(CretaeHooObject);/ 下面调用dll的输出函数,来创建接口对象if (!proc(ppFoo)throw CreateFooObject() error!;return TRUE;/ 到此,接口对象创建成功!catch(LPCSTR szMsg)lstrcpyn(m_szErrMsg, szMsg, sizeof(m_szErrMsg);return FALSE;catch(.)lstrcpyn(m_szErrMsg, Unknown Error!, sizeof(m_szErrMsg);return FALSE;char*GetErrMsg() return m_szErrMsg;/ 返回错误信息private:HINSTANCEm_hDll;/ 动态连接库的加载与否标志charm_szErrMsg128;/ 错误信息2、 关于Dll的多次加载问题的考虑由于,LoadLibrary和FreeLibrary,在Windows系统内部有一个计数,所以只要LoadLibrary和FreeLibrary调用次数一致即可,不会产生dll资源多次加载或释放不正确等问题。详细见之“LoadLibrary and AfxLoadLibrary”,:mk:MSITStore:C:Program%20FilesMicrosoft%20Visual%20StudioMSDN2001JUL1033vccore.chm:/html/_core_loadlibrary_

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论