在Regular Dlls中拦截键盘消息

来源:互联网 发布:网络投融资平台 编辑:程序博客网 时间:2024/05/19 21:02
下文参考了MSDN TN011以及TN033,在此感谢原作者。

利用Visual C++ 6.0我们能够创建两种Dll项目:Regular Dlls以及MFC Extended Dlls。通过将应用逻辑封装至Regular Dlls,库提供方得以仅暴露所需的接口,并且具备独立于宿主程序的灵活性。Regular Dlls往往应用于这样的场合:调用方作为一个完整的Win32应用程序,其未必调用任何MFC库函数;而被调用方则期望建立于MFC库基础之上。此时可 以考虑将被调用方构建为一个Regular Dll。譬如笔者最近接手的一个项目,需求是建立一个plugged的界面系统,该GUI在不依赖原系统底层组件的前提下允许采用其它任何第三方组件快 速进行开发。由于有开发周期的限制,加上其他技术因素上的权衡,笔者最终决定采用MFC来做。MFC的优点是众所周知的,在本地windows平台上开发 用户界面,其一度享有工业标准的待遇。由于该项目中的GUI子系统必须独立于底层的业务逻辑,并且能够轻易实现可插拔,所以宜于实现为Regular Dll。该GUI与后端业务逻辑子系统之间采用XML文件来传输数据,从而保持了系统内部各子系统之间的松耦合。


上图呈现了一个两层结构的基本组成,黄色部分为所需实现的plugged GUI。可以将该模块看作某第三方API,它向主程序提供一些函数,使得主程序能够顺利渲染其主要的图形用户界面,并且能从此界面上获取一些配置信息与初始化信息等。然而,上述模块又不仅仅是一组API,它除了具有结构化的接口体系以外,还具有完整的处理逻辑,能够对于用户输入做出一般响应,从而在它运行时,主程序完全处于空闲(除了接收常规的windows消息外)状态,而它则成为了应用程序的“主角”。为了达到上述目标,将界面子系统简单地实现为一些widget类的集合是不尽如人意的。因为这样便假定了后端子系统会应对各种windows消息的纷至沓来,而对于后端子系统的依赖性应当首先被排除。于是,我们需要一类功能强大的组件,它提供了完整的界面类体系;能够独立对付windows消息;能够灵活地插入主程序。基于这三点原因,选择MFC作为起点无疑是正确的。

The MFC library defines the standard Win32 DllMain entry point that initializes your CWinApp derived object as in a normal MFC application. Place all DLL-specific initialization in the InitInstance member function as in a normal MFC application.

Note that the CWinApp::Run mechanism doesn't apply to a DLL, since the application owns the main message pump. If your DLL brings up modeless dialogs or has a main frame window of its own, your application's main message pump must call a DLL-exported routine that calls CWinApp::PreTranslateMessage.

上面这段话摘自MSDN TN011,简而言之,其阐明了采用MFC建立Regular Dlls过程中的一些基本问题。譬如,为什么键盘消息不能顺利到达MFC Regular Dll的消息处理函数?答案正在于CWinApp::Run机制并不适用于DLL。因此为了更好地使用前文所述的MFC Based Regular Dll of GUI sub-system,必须对宿主程序的主消息循环稍作修改。拿键盘消息为例,我们需要在消息到达::TranslateMessage函数之前即调用dll的CWinApp::PreTranslateMessage函数,从而使得键盘消息诸如WM_KEYDOWN、WM_CHAR、WM_KEYUP等成功路由至其各自归宿,进而得到正确的处理。

举例如下:

extern "C" BOOL FAR PASCAL EXPORT FilterDllMsg(LPMSG lpMsg)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    TRY
    
{
        
return AfxGetThread()->PreTranslateMessage(lpMsg);
    }

    END_TRY
    
return FALSE;
}


BOOL CTheApp::PreTranslateMessage(MSG
* pMsg)
{
    
// special filter for DLL
    if (CWinApp::PreTranslateMessage(pMsg))
        
return TRUE;

    
return FilterDllMsg(pMsg);
}

此例摘自MSDN Samples中的DLLTrace项目。该项目的宿主端亦采用MFC编写,故而为了达到前文所述的效果,只需覆写宿主程序的PreTranslateMessage函数,并在其中调用dll的PreTranslateMessage函数即可。这样一来,包括键盘消息在内的大部分windows消息均可由dll自主进行响应了。
原创粉丝点击