zoukankan      html  css  js  c++  java
  • ATL对WINDOWS窗口的封装

    ATL包装了WINAPI中与创建和操作窗口、对话框以及WINDOWS控制有关的部分。ATL窗口类还包含了诸如子类化和超类化这样的高级特性。

    一、Windows应用程序的结构

    入口点——_tWinMain,它提供应用程序的HINSTANCE、命令行参数和指示如何显示主窗口的标志。

    调用RegisterClass注册主窗口类。

    调用CreateWindow创建主窗口。

    调用ShowWindowUpdateWindow来显示主窗口。

    一个分发消息的消息循环。

    一个处理主窗口消息的过程。

    一组消息处理函数,用来处理窗口感兴趣的消息。

    调用DefWindowProcWindows处理我们不感兴趣的消息。

    一旦主窗口被销毁,调用PostQuitMessage

    ATL 把以上的过程调用封装成一个对象。它包含窗口类(用WNDCLASSEX结构来表示)、窗口对象(用HWND来表示)和成员函数调用(通过对 WndProc的调用表示)。ATL提供了一组窗口类,CWindowCWindowImplCWinTraitsCWinTraitsOR CDialogImplCSimpleDialogCContainedWindowT。以及CWindowImplRoot CWindowImplBaseTCDialogImplBaseT等辅助类。

     

    二、CWindow

    1、封装HWND及相关的一些API函数

    1Cwindow类包含了公共成员HWND hWnd

    2CWindow类封装了User32 API函数。
    2
    CWindow类的使用

    //Needed to use ATL windowing classes

    #include <atlbase.h>

    extern CComModule _Module;

    #include <atlwin.h>

    ATL窗口类定义在atlwin.h中,依赖于atlbase.h

    _Module实例保存了应用的HINSTANCE实例以及ATL模块的初始化和终结。

    _Module.Init(0,hinst);    //初始化ATL模块

     

    CWindow win; //对象构造

    win.Create( “button”, NULL, CWindow::rcDefault, “Click me”,WS_CHILD ); //窗口创建

     

    _Module.Term();    //终结ATL模块

     

    CWindows在窗口创建时就决定了窗口收到消息后的处理方法,无法改变。从CWindows类派生出来的CWindowsImpl类提供指定窗口的新行为。通过给窗口类添加消息映射达到了添加新行为的方法。扩展或改变一个窗口实例的行为的方法:

    1)写一个新的ATL窗口类,并子类化这个已经存在的实例。

    2)使这个实例成为被包含的窗口,所有到这个实例的消息都会通过容器窗口。

    3)采用消息反映射,当一个窗口收到消息后不做处理,而反射给发送这个消息的窗口自己处理,这种技术可用于创建自包含的控件。

     

    三、CWindowImpl

    CWindowImpl类从CWindow派生出来的,并且提供了窗口类注册和消息处理两个特性。

    1、窗口类注册

    Create成员函数里注册了窗口类。

    1)窗口信息

    CWndClassInfo结构:

    struct _ATL_WNDCLASSINFOA
    {
    WNDCLASSEXA m_wc;
    LPCSTR m_lpszOrigName;
    WNDPROC pWndProc;
    LPCSTR m_lpszCursorID;
    BOOL m_bSystemCursor;
    ATOM m_atom;
    CHAR m_szAutoName[32];
    ATOM Register(WNDPROC* p)
    {

    return AtlModuleRegisterWndClassInfoA

    (&_Module, this, p);

    }

    };

    m_wc表示窗口类的结构,在手工注册类时,用它来进行注册。

    m_atom用于确定类是否被注册。

    DECLARE_WND_CLASS宏定义函数GetWndClassInfo()获得一个CWndClassInfo实例。

    DECLARE_WND_CLASS_EX宏提供了但对DECLARE_WND_CLASS宏的扩展。

    这两个宏对窗口类的信息都提供了默认值,要修改其他的值,可以用函数GetWndClassInfo函数修改CWndClassInfo结构。

    2)窗口特性(Window Traits

    CWinTraits类保存了一个风格(style)和扩展风格(extended style

    3)窗口过程(Window Procedure

    每个窗口都有一个窗口过程处理窗口消息。这个窗口过程是在窗口注册期间设置 WNDCLASSEX结构的lpfnWndProc成员。DECLARE_WND_CLASSDECLARE_WND_CLASS_EX宏中用 StartWindowProc函数设置lpnWndProc成员。StartWindowProc函数完成了HWND和对象的this指针之间的映射。

    CreateWindow[Ex]时之前通过_Module.AddCreateWndData(&m_thunk.cd, this);将对象的this指针存在_Module维护的列表中。在调用窗口过程的时候在StartWindowProc中调用 _Module.ExtractCreateWndData()获得this指针。

    ATL采用一组动态创建的汇编指令thunk实现了对this指针的存储和查找。每个CWindowImpl对象有自己的thunkthunk就是窗口的过程。thunk保存在_WndProcThunk结果里面。

    struct _WndProcThunk
    {
    DWORD   m_mov;          // mov dword ptr

    [esp+0x4], pThis (esp+0×4 is hWnd)
    DWORD   m_this;         //
    BYTE    m_jmp;          // jmp WndProc
    DWORD   m_relproc;      // relative jmp
    };

    StartWindowProc函数里,通过pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);将窗口的this指针和窗口消息处理函数WindowProc初始化到thunk静态结构里。

    Windows消息路由路径:

    Windows — WM_XXX –> CWindowImpl派生类的对象的thunk — 函数调用 –> CWindowImpl的静态成员函数WindowProc — 成员函数调用 –> CWindowImpl派生类的ProcessWindowMessage

    4windows超类化

    声明一个窗口类并且创建这个类的一个实例。超类化是复制已有窗口类的WNDCLASSEX结构并且赋予它自己的名字和自己的WndProc”。窗口收到一条消息后,消息被路由到新的WndProc,如果新的WndProc不处理该消息,消息将路由到原来的WndProc

    2、消息处理

    ATL的消息处理通过CWindowImpl派生类提供的ProcessWindowMessage函数分发处理各个类型的消息。ATL定义了一些宏便于组合处理消息链。

    #define BEGIN_MSG_MAP(theClass) \
    public: \
    BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg,

    WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD

    dwMsgMapID = 0) \
    { \
    BOOL bHandled = TRUE; \
    hWnd; \
    uMsg; \
    wParam; \
    lParam; \
    lResult; \
    bHandled; \
    switch(dwMsgMapID) \
    { \
    case 0:

    #define END_MSG_MAP() \
    break; \
    default: \
    ATLTRACE2(atlTraceWindowing, 0,

    _T(“Invalid message map ID (%i)\n”), dwMsgMapID); \
    ATLASSERT(FALSE); \
    break; \
    } \
    return FALSE; \
    }

    通过BEGIN_MSG_MAPEND_MSG_MAP定义了消息链的框架。BOOL bHandled实现了一个是否这条消息还需要后续处理的标记。作为消息处理函数的参数,如果需要后续处理将该值设为false

     

    BEGIN_MSG_MAP( CBase )

    MESSAGE_HANDLER( WM_CREATE, OnCreate1 )

     MESSAGE_HANDLER( WM_PAINT, OnPaint1 )

     ALT_MSG_MAP( 100 )

     MESSAGE_HANDLER( WM_CREATE, OnCreate2 )

    MESSAGE_HANDLER( WM_PAINT, OnPaint2 )

    ALT_MSG_MAP( 101)

    MESSAGE_HANDLER( WM_CREATE, OnCreate3 )

    MESSAGE_HANDLER( WM_PAINT, OnPaint3 )

    END_MSG_MAP()

    如上,基类的消息映射表由3节组成:一个默认的消息映射表(隐含的标识为0)和两个可选的消息映射表(标识为100101)。

    如果我们在多个类中都要响应WM_CREATE消息,但是不同的类需要基类提供不同的处理,怎么办呢?为了解决这个问题,ATL使用了可选的消息映射:将消息映射表分成很多节,每一节用不同的数字标识,每一节都是一个可选的消息映射表。

    当你链接消息映射表时,采用以下的方案的标识,如下:

    class CDerived: public CBase {

    BEGIN_MSG_MAP( CDerived )

    CHAIN_MSG_MAP_ALT( CBase, 100 )

    END_MSG_MAP()

    }

    CDerived的消息传入CBase后会被100的消息表内的函数处理。

    CHAIN_MSG_MAP( CBase ) 可以把没被处理的消息从被派生的类传入基类CBase

    CHAIN_MSG_MAP_ALT( CBase, 100 ) 可以把没被处理的消息从被派生的类传入基类CBase,由基类内消息标识为100的处理函数处理。

    四、CDialogImpl

    1、 模式

    对话框有两种模式:有模式和无模式。
    有模式:当对话框可见时,父窗口不可访问。
    无模式:父窗口在对话框可见期间仍然可以被访问。
    2
    、交换数据

    有模式对话框的交换数据的操作:

    1)应用程序创建一个CDialogImpl派生类的实例。
    2)应用程序向对话框对象的数据成员复制一些数据。
    3)应用程序调用DoModal
    4)对话框处理WM_INITDIALOG,把数据成员复制到子控制中。
    5)对话框处理OK按钮时会确认子控制保存的数据的有效性。如果数据无效,那么对话框向用户报错并且让用户继续尝试,直至使之正确或者用户点击Cancel取消。
    6)如果数据是有效的,那么数据被复制回到对话框的数据成员,并且终止对话框。
    7)如果应用程序从DoModal得到了IDOK,应用把对话框的数据成员复制到自己的拷贝中。

    无模式对话框的交互数据操作:

    1)应用程序创建一个CDialogImpl派生类的实例。
    2)应用程序向对话框对象的数据成员复制一些数据。
    3)应用程序调用Create
    4)对话框处理WM_INITDIALOG,把数据成员复制到子控制中。
    5)对话框处理Apply按钮时会确认子控制保存的数据的有效性。如果数据是无效,那么对话框向用户报错并且让用户继续尝试,直至使之正确或者用户点击Cancel取消。
    6)如果数据是有效的,那么数据被复制回到对话框的数据成员,并通知应用从对话框读取更新后的数据。
    7)应用程序接到通知时,应用把对话框的数据成员复制到自己的拷贝中。

    五、CContainedWindow

    CContainedWindow 的对象让它的父窗口处理消息,让一个已有的窗口类处理父窗口传递的消息。父窗口可以创建一个子窗口类的实例,然后把它子类化,子窗口的消息会通过父窗口的 消息映射表路由。父窗口通过替代消息映射表辨别来自子窗口和自己的消息,每个CContainedWindow分配一个消息映射ID,并且它的消息被路由 到父窗口消息映射表的相应替代部分。

    1)创建被包含的窗口
    调用CContainedWindowCreate成员创建被包含的窗口。

    2)子类化被包含的窗口
    子类化不创建新的窗口,它仅仅是截取单个窗口的消息。我们创建某个类的一个窗口,并且使用SetWindowLong(GWL_INITDIALOG)把原来的窗口过程替换成我们自己的窗口过程。

  • 相关阅读:
    springboot跨域解决
    python实现LRUCache
    oracle整库统计库表数据量--存储过程
    oracle整库统计库表各个字段数据最大长度--存储过程
    mysql数据库初始化脚本分组批量执行
    maven profile 的作用
    配置中包含maven属性,在idea中本地启动无法正常获取配置
    个人随笔
    Java接口interface field及method
    如何将.net core项目部署到IIS上?
  • 原文地址:https://www.cnblogs.com/zdxster/p/1945878.html
Copyright © 2011-2022 走看看