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毫秒