ATL 窗口类源码浅析

图1

对于典型的Win32应用程序而言,一般包括WinMain,窗口类注册,创建显示窗口,消息循环,消息处理。

下面是ATL Internals上面对上图的说明:

ATLWin32窗口API的封装如上图,其中粗体子的类比较重要,而其它的则是作为一般的辅助类,当然这个图并没有包括所有的内容,比如窗口类注册,消息循环等,下面我会一一道来。

1WinMainATL窗口程序中没有太大的变化,并没有先MFC那样直接Linkexe中,而是暴露给程序使用。

2窗口类注册在ATL中使用了一个如下的结构:

struct _ATL_WNDCLASSINFO

{

WNDCLASSEX m_wc;

LPCSTR m_lpszOrigName;

WNDPROC pWndProc;

LPCSTR m_lpszCursorID;

BOOL m_bSystemCursor;

ATOM m_atom;

CHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];

ATOM Register(WNDPROC* p)

{

return AtlWinModuleRegisterWndClassInfoA(&_AtlWinModule, &_AtlBaseModule, this, p);

}

};

另外在通过typedef定义为:typedef _ATL_WNDCLASSINFOW CWndClassInfo;

此结构是通过下面的define引入的:

/

// CWndClassInfo - Manages Windows class information

#define DECLARE_WND_CLASS(WndClassName) /

static ATL::CWndClassInfo& GetWndClassInfo() /

{ /

static ATL::CWndClassInfo wc = /

{ /

{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, /

  0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, /

NULL, NULL, IDC_ARROW, TRUE, 0, _T("") /

}; /

return wc; /

}

#define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd) /

static ATL::CWndClassInfo& GetWndClassInfo() /

{ /

static ATL::CWndClassInfo wc = /

{ /

{ sizeof(WNDCLASSEX), style, StartWindowProc, /

  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, /

NULL, NULL, IDC_ARROW, TRUE, 0, _T("") /

}; /

return wc; /

}

#define DECLARE_WND_SUPERCLASS(WndClassName, OrigWndClassName) /

static ATL::CWndClassInfo& GetWndClassInfo() /

{ /

static ATL::CWndClassInfo wc = /

{ /

{ sizeof(WNDCLASSEX), 0, StartWindowProc, /

  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, /

OrigWndClassName, NULL, NULL, TRUE, 0, _T("") /

}; /

return wc; /

}

CWindowImpl中有定义DECLARE_WND_CLASS(NULL),因此任何从CWindowImpl派生的类都包含一个GetWndClassInfo函数并返回一个static CWndClassInfo用于注册窗口类,这也比较符合所有的窗口类产生的窗口对象都是一个样式的,比如你用BUTTON作为窗口类创建的肯定就是一个按钮了。上面第三个define是用来SuperClass用的,至于SuperClassSubClass的区别可以在MSDN上面找到,主要就是前者对整个窗口类而言,而后者只对某个窗口而言。

整个窗口类的注册比较复杂,下面是我根据源码分析的流程

 

图2

3窗口创建及显示部分,这里主要由CWindow来实现,这个和MFCCWnd不同处在于,CWnd把不仅包括对HWND形式API的封装还包括内部的CWndHWND之间的映射,消息映射,OLE等等,而CWindow仅仅只包括一个HWND数据成员,只对API进行了很浅的封装基本上就是一些ShowWindowSetWindowText之类的API的一个包装,而且CWindow在析构时也不会销毁窗口,对于窗口的消息映射等方面没有做了任何处理。

4消息映射部分,这里从图1可以看到一个CMessageMap的类,它负责窗口的消息映射

class ATL_NO_VTABLE CMessageMap

public:

virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,

LRESULT& lResult, DWORD dwMsgMapID) = 0;

};

CWindowImplRoot直接从CWindowCMessageMap派生,因此基本上具体有了窗口和处理消息的功能,但是为了减少模板带来的代码膨胀,这个类以及下面的CWindowImplBaseT只是起辅助作用,其中前者实现了对消息反射以及消息转发,消息反射是为了将子窗口中的通知消息反射回去以便于实现复用的控件,消息转发主要是在实现组合窗口中传递通知消息用的,当一个子窗口的父窗口还有父窗口,而这时子窗口消息又是要在最外层窗口中处理时就可以通过转发消息,两者的实现也不复杂:

template <class TBase>

LRESULT CWindowImplRoot< TBase >::ForwardNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

LRESULT lResult = 0;

switch(uMsg)

{

case WM_COMMAND:

case WM_NOTIFY:

case WM_PARENTNOTIFY:

case WM_DRAWITEM:

case WM_MEASUREITEM:

case WM_COMPAREITEM:

case WM_DELETEITEM:

case WM_VKEYTOITEM:

case WM_CHARTOITEM:

case WM_HSCROLL:

case WM_VSCROLL:

case WM_CTLCOLORBTN:

case WM_CTLCOLORDLG:

case WM_CTLCOLOREDIT:

case WM_CTLCOLORLISTBOX:

case WM_CTLCOLORMSGBOX:

case WM_CTLCOLORSCROLLBAR:

case WM_CTLCOLORSTATIC:

lResult = GetParent().SendMessage(uMsg, wParam, lParam);

break;

default:

bHandled = FALSE;

break;

}

return lResult;

}

template <class TBase>

LRESULT CWindowImplRoot< TBase >::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

HWND hWndChild = NULL;

switch(uMsg)

{

case WM_COMMAND:

if(lParam != NULL) // not from a menu

hWndChild = (HWND)lParam;

break;

case WM_NOTIFY:

hWndChild = ((LPNMHDR)lParam)->hwndFrom;

break;

case WM_PARENTNOTIFY:

switch(LOWORD(wParam))

{

case WM_CREATE:

case WM_DESTROY:

hWndChild = (HWND)lParam;

break;

default:

hWndChild = GetDlgItem(HIWORD(wParam));

break;

}

break;

case WM_DRAWITEM:

if(wParam) // not from a menu

hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;

break;

case WM_MEASUREITEM:

if(wParam) // not from a menu

hWndChild = GetDlgItem(((LPMEASUREITEMSTRUCT)lParam)->CtlID);

break;

case WM_COMPAREITEM:

if(wParam) // not from a menu

hWndChild =  ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem;

break;

case WM_DELETEITEM:

if(wParam) // not from a menu  

hWndChild =  ((LPDELETEITEMSTRUCT)lParam)->hwndItem;

 

break;

case WM_VKEYTOITEM:

case WM_CHARTOITEM:

case WM_HSCROLL:

case WM_VSCROLL:

hWndChild = (HWND)lParam;

break;

case WM_CTLCOLORBTN:

case WM_CTLCOLORDLG:

case WM_CTLCOLOREDIT:

case WM_CTLCOLORLISTBOX:

case WM_CTLCOLORMSGBOX:

case WM_CTLCOLORSCROLLBAR:

case WM_CTLCOLORSTATIC:

hWndChild = (HWND)lParam;

break;

default:

break;

}

if(hWndChild == NULL)

{

bHandled = FALSE;

return 1;

}

ATLASSERT(::IsWindow(hWndChild));

return ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);

}

template <class TBase>

BOOL CWindowImplRoot< TBase >::DefaultReflectionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)

{

switch(uMsg)

{

case OCM_COMMAND:

case OCM_NOTIFY:

case OCM_PARENTNOTIFY:

case OCM_DRAWITEM:

case OCM_MEASUREITEM:

case OCM_COMPAREITEM:

case OCM_DELETEITEM:

case OCM_VKEYTOITEM:

case OCM_CHARTOITEM:

case OCM_HSCROLL:

case OCM_VSCROLL:

case OCM_CTLCOLORBTN:

case OCM_CTLCOLORDLG:

case OCM_CTLCOLOREDIT:

case OCM_CTLCOLORLISTBOX:

case OCM_CTLCOLORMSGBOX:

case OCM_CTLCOLORSCROLLBAR:

case OCM_CTLCOLORSTATIC:

lResult = ::DefWindowProc(hWnd, uMsg - OCM__BASE, wParam, lParam);

return TRUE;

default:

break;

}

return FALSE;

}

上面的DefaultReflectionHandler是在子窗口中对不感兴趣的反射消息进行默认的处理。

CWindowImplBaseT你用Thunk(这个原理也不是很懂大致就是用一些汇编方式的技巧修改内存中的某些数据吧)实现了窗口消息和对应的CWindowImplBaseT对象成员函数之间的消息映射,还有就是窗口的subclass和创建等。最后的CWindowImpl仅仅提供了一个Create函数在调用基类Create之前先进行窗口类的注册也就是图2的一系列流程。

这里还有CWinTraitsCWinTraitsOR它们主要是进行窗口样式的辅助设置。

5程序结构

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,

                   LPSTR szCmdLine, int nCmdShow)

{

CMyWindow wndMain; //这里CMyWindow派生自CWindowImpl

MSG msg;

 

    // Create & show our main window

    if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, 

                                 _T("My First ATL Window") ))

    {

        // Bad news, window creation failed

        return 1;

    }

 

    wndMain.ShowWindow(nCmdShow);

    wndMain.UpdateWindow();

 

    // Run the message loop

    while ( GetMessage(&msg, NULL, 0, 0) > 0 )

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

 

    return msg.wParam;

}

差不多大部分的ATL Window部分分析完了,虽然没怎么写文字,但是看一大串模板代码还是够呛的,尤其是注册窗口那部分绕来绕去的,+_+

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要下载ATL VC示例源码,可以按照以下步骤进行操作: 1. 打开您的网页浏览器,如Google Chrome或Microsoft Edge。 2. 在搜索栏中输入“ATL VC示例源码下载”或访问ATL VC官方网站。 3. 导航至官方网站的下载页面或示例源码页面。 4. 在该页面中浏览示例源码列表,找到您感兴趣的示例项目。 5. 点击所选示例项目的下载按钮或链接。 6. 系统将开始下载示例源码文件到您的计算机。 7. 等待下载完成,时间长短取决于您的网络连接速度和文件大小。 8. 找到已下载的示例源码文件,通常会保存在计算机的默认“下载”文件夹中。 9. 解压缩下载的示例源码文件,通常可以使用Windows内置的解压缩工具或第三方解压缩软件。 10. 进入解压缩后的示例源码文件夹,通过阅读源代码来理解和学习ATL VC示例项目的实现方式。 希望以上步骤能帮助您成功下载并使用ATL VC示例源码!如有需要,也可以参考官方网站上的下载指南或寻求相关技术支持。 ### 回答2: 您好!要下载 ATL VC 示例源码,您可以按照以下步骤进行操作: 1. 打开您的网络浏览器,前往 ATL VC 示例的官方网站。您可以使用搜索引擎搜索 "ATL VC 示例" 找到相关网站。 2. 在官方网站上,浏览或搜索示例源码下载页面。有些网站会直接在首页或导航栏上提供示例源码的下载入口,您可以直接点击进入。 3. 一旦找到下载入口,点击进入示例源码下载页面。检查页面上是否有提供示例源码的链接或按钮。 4. 点击链接或按钮,开始下载示例源码。通常,示例源码会以压缩文件的形式提供,如 ZIP 或 RAR 格式。 5. 下载完成后,解压缩源码文件。您可以使用常见的解压缩软件,如 WinRAR 或 7-Zip。 6. 解压缩完成后,您将获得一个包含示例源码的文件夹。您可以通过浏览文件夹的内容来了解其中的示例代码。 请注意,下载示例源码可能需要一定的时间,具体取决于您的网络速度和源码文件的大小。另外,确保从官方网站下载源码,以确保其完整性和安全性。 希望以上信息能够对您有所帮助!如有其他问题,请随时告知。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值