Delphi中模块句柄管理,及FindClassHInstance详解(转)
第一部分、问题的提出:GetModuleHandle()和全局变量HInstance都只能得到Exe的模块句柄。
在设计企业开发框架时,因为采用的是Exe,BPL,DLL的结构:一个Exe和Bpl类库,及多个Dll窗口模块。在Bpl开发框架中设计了基类窗口、单表、主从表、多平行表、对话框、数据编辑对话框等等模板,而这些窗口统一由窗口类工厂创建释放,并由主窗口模板创建类工厂接口实例进行管理。创建的窗口有二种形式:MDI子窗体和内嵌在页控件中的普通窗口。
其中实际的业务处理功能模块放在DLL中,这样做好处有二点:便于Team开发,每人负责一个模块对应一个DLL;Dll方便动态调入执行,且比BPL更高效~可用VS中的Depends工具比较看看就知道了!!
这样,窗口类工厂除了要管理具体的窗口,还得需要处理模块的装卸,不可避免的须与模块句柄打交道。当我们用GetModuleHandle这个API发现给传入它空指针参数,并不是返回调用模块DLL或BPL的句柄,而一直返回的是Exe的(更正:其实应该都是Bpl的句柄,因为这部分代码都是在框架代码BPL中)。试着用全局变量HInstance,和GetModuleHandle的效果一样。
Google了一下,只找到了C++的解决方法:传入函数指针,用VirtualQuery返回函数所在模块基地址,也即是模块句柄。而开发框架的基类窗口模板需要通知类工厂窗口的释放,这就需要向类工厂传递窗口所在Dll的模块句柄(DLL有一规则:哪个模块创建则由哪个模块释放,不然.....)。即使强行在基类窗口增加一个全局空函数(什么也不干的),以得到它的指针来获取模块句柄,但基类窗口是作为模板封装在BPL中,这样做得到只是Bpl的句柄。
第二部分、解决之道
方法一,最直接最笨的方法:动态调入Dll时保存句柄并传入到Dll,没什么好说的。
方法二,FindClassHInstance( ),此函数在System.pas单元中,而且可用于Win 和LinuX ,它在Win下的实现也是VirtualQuery函数,不同的是它将类引用TClass作为指针传给VirtualQuery( )。哈,类引用,正好用在窗口模板中。在开发框架中,每个由窗口类工厂直接创建的窗口都须注册,这样外部只须传入窗口类名就可调用它。在基类窗口模板这样就可动态得到实际窗口所在的模块句柄了:
FindClassHInstance(Self.ClassType);
得到模块句柄后,向类工厂发送消息通知窗口的释放事件。
小贴士:其实Delphi中,很多东东的本质都是指针:接口实例、对象实例、类引用TClass、函数指针、方法的代码指针,理解了,我们用TLIst很好进行管理!
补:前些日子在武稀松的博客上看到一新的方法,汗(还是没高手的钻研精神,向他们学习)
HMODULE hModule = NULL;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCSTR)&XXX, //可以是任何函数,全局变量等的地址
&hModule);
该方法的原文链接:
http://blog.csdn.net/wr960204/archive/2008/04/21/2312284.aspx
后记:
1、最近突然发现全局变量HInstance确实为当前模块句柄,查看文档也是这样说的:
HInstance provides a unique instance handle for the application or library
2、Api GetModuleHandleEx函数只支持到Xp
3、System单元中FindHInstance函数可以传入局部函数地址和全局变量地址,为什么不能是局部变量地址呢:因为D很多情况下将局部变量放在寄存器或堆栈中!