Programming 版 (精华区)
发信人: SwordLea (飞刀李), 信区: Programming
标 题: 编码的境界——从MOC到COM(原创).8
发信站: 哈工大紫丁香 (2004年01月17日09:25:52 星期六), 站内信件
对于DLL 工程的创建与使用,相信已经有太多的图书资料进行深入讲解,如
果有些同学认为购买纸版图书不利于有悖环境保护意识,可以去VC版在线阅读或
者去202.118.224.241 下载电子版。
假设我们创建的全部DLL 都有一个名为MyEntry 的导出函数,主程序只需要
在App 的初始化代码部分扫描当前目录或指定文件夹下的所有DLL 扩展名文件,
如果LoadLibrary成功,则调用GetProcAddress(Dll句柄,"MyEntry");否则继续
扫描下一个。若GetProcAddress失败,记得要FreeLibrary,否则保存在pMyEntry
指针里。
这种方式很象目前流行的插件技术,如果你曾经对WinAmp加入一个小插件就
支持APE 格式的播放而感觉惊奇,上面这几行文字应该会让你豁然开朗。
好了,现在我们已经得到了一个或者多个MyEntry 函数的入口地址,怎么使
用它们完全取决于个人好恶。这里有几种方案供选择:
1、向主程序注册机制。
App 管理一个全局表(可以使用vector 或者CList),让我们假设该表名为
g_vDllFunc,再对该表提供一个全局的管理器类,名为g_DllFuncsManager, 该
类至少应提供以下几个成员函数:
Register(LPVOID pDllFunc, UINT uEventID); // 注册DLL函数被调用事件
UnRegister(LPVOID pDllFunc, UINT uEventID); // 注销DLL函数事件
Notify(UINT uEventID); // 通知已注册的DLL函数某事件发生
各成员函数的实现都非常简单,只是在维护全局表g_vDllFunc。如果你使用
vector实现的话分别是:不存在就push_back,存在就erase,对于所有uEvent匹
配项调用Dll导出函数注册的函数指针。
将管理器类接口信息写入一个公共访问的Interface.h头文件中:
// Interface.h
class IDllFuncsManager
{
// 注册DLL函数被调用事件
Register(LPVOID pDllFunc, UINT uEventID) = 0;
// 注销DLL函数事件
UnRegister(LPVOID pDllFunc, UINT uEventID) = 0;
// 通知已注册的DLL函数某事件发生
Notify(UINT uEventID) = 0;
}
由于我们的目的只是提供一个接口,所以各成员函数写成纯虚函数的形式,
告诉编译器我们不打算写IDllFuncsManager的各函数的实现。
现在,为前面的提到的管理器类(CDllFuncsManager),加上基类: public
IDllFuncsManager. 也许有同学对我先写派生类后写基类的作法表示异议,但事
实证明这样的效率也许要高一些,否则我们将会试图写一个尽可能完美的基类,
然后发现有些成员函数根本用不到,可它们会一直留在那里,成为下几个版本的
累赘。
Dll 导出函数的实现也需要做一点小改动:a.加入对interface.h 的包含;
b.导出函数的参数格式为IDllFuncsManager *pDFM。前面对Dll 导出函数的调用
则写成酱子:
pMyEntry(&g_DllFuncsManager);
看到这里,我想许多同学都已经开始晕晕了。画个图说明一下吧,不太标准,
希望能有人看得懂。左图控制权在App手中,pMyEntry(&g_DllFuncsManager )语
句执行后,控制权转移到Dll手里,Dll 把各事件及对应的处理函数注册给App。
------------------ ------------------
| App | | Dll |
|-----------------| |-----------------|
|g_DllFuncsManager| |Func1,Func2,FuncN|
|g_vDllFunc | |Event1,Event2... |
------------------ ------------------
| |
V V
------------------- ------------------
| 扫描DLL | | pDFM != NULL |
------------------- ------------------
| 成功 | True
V V
------------------- ------------------
| 取MyEntry地址 | | pDFM->Register |
| -> pMyEntry | | Func1,Event1 |
------------------- | FuncN,EventN |
| 成功 -------------------
V
------------------------------
|pMyEntry(&g_DllFuncsManager) |
------------------------------
这里使用了事件,有些类似于Windows 中的消息。举个例子说明其使用方法,
比如我们定义了枚举型的各种事件(在Interface.h 中定义,以便App 与Dll 都
能访问到):eventFileOpen ,eventFileClose,在某DLL 注册事件、函数时分
别与功能函数funcFileOpen,funcFileClose 绑定到一起。在App 中的Doc 类,
记得把g_DllFuncsManager->Notify (eventFileOpen )写入OnDocumentOpen函
数。当然,如果没有使用文档、视体系支持的话,也可以把它写到App 打开文件
菜单处理函数之后。这里要求在调用Dll 导出函数注册的函数指针时,传入被打
开的文件名。我们一般不直接写入字符串,而是传入一个规定好的结构,使被调
用函数能一次取得尽可能多的信息。或者我们直接传入一个类指针,被调用函数
想得到什么,各取所需吧!
此时,你的App 基本不需要做什么改动了,打算加入新功能处理文件,只需
要新写个DLL 丢到当前或指定的文件夹。如果担心对文件处理重复进行,也可以
在调用已注册函数时,根据事先约定的返回值决定是否结束处理。
(to be continue……)
--
如果程序员来到了程序设计版,
那么他就不会再牛,
更不会随便给予了。
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.246.232]
※ 修改:·SwordLea 於 01月17日09:27:09 修改本文·[FROM: 202.118.246.232]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.380毫秒