ATL对窗口的封装

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

一、Windows应用程序的结构

入口点——_tWinMain,它提供应用程序的HINSTANCE、命令行参数和指示如何显示主窗口的标志。
调用RegisterClass注册主窗口类。
调用CreateWindow创建主窗口。
调用ShowWindow和UpdateWindow来显示主窗口。
一个分发消息的消息循环。
一个处理主窗口消息的过程。
一组消息处理函数,用来处理窗口感兴趣的消息。
调用DefWindowProc让Windows处理我们不感兴趣的消息。
一旦主窗口被销毁,调用PostQuitMessage。

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

二、CWindow类
1、封装HWND及相关的一些API函数
Cwindow类包含了公共成员HWND hWnd。
CWindow类封装了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模块
_Module.Term();    //终结ATL模块

三、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_CLASS和DECLARE_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对象有自己的thunk,thunk就是窗口的过程。thunk保存在_WndProcThunk结果里面。
struct _WndProcThunk
{
    DWORD   m_mov;          // mov dword ptr

[esp+0x4], pThis (esp+0x4 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

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

(5)消息处理
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_MAP与END_MSG_MAP定义了消息链的框架。

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

WM_COMMAND消息:
wNotifyCode  = HIWORD(wParam);    //notification code
wID = LOWORD(wParam);    //item,control, or accelerator
            // identifier
hwndCtl = (HWND)lParam;    // handle of control

WM_NOTIFY消息:
idCtrl = (int)wParam;    // control identifier
pnmh = (LPNMHDR)lParam;    // address of NMHDR structure
typedef struct tagNMHDR {
    HWND hwndFrom;    // handle of the control
    UINT idFrom;    // control identifier
    UINT code;    // notification code
} NMHDR;
四、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)创建被包含的窗口
调用CContainedWindow的Create成员创建被包含的窗口。
(2)子类化被包含的窗口
子类化不创建新的窗口,它仅仅是截取单个窗口的消息。我们创建某个类的一个窗口,并且使用SetWindowLong(GWL_INITDIALOG)把原来的窗口过程替换成我们自己的窗口过程。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值