剖析WINX的Hello程序

概述

我们已经介绍了Windows SDK的Hello程序,它的流程主要分为三个步骤:

  1. 注册窗口类(RegisterClass)。并且我们详细解释了为何要有窗口类,为何要RegisterClass
  2. 创建并显示窗口(CreateWindow and ShowWindow)。
  3. 消息循环(MessageLoop)。即:取得消息 -> 分派消息 -> 处理消息。

这里,我们就要结合WINX的Hello程序,把整个流程串一遍。

作为比较,我想温习一下ATL/WTL的Hello程序。我们在此提供了几篇剖析ATL/WTL的Hello程序的好文章:

WINX的Hello程序

 

#define  WINX_USE_APPMODULE
#include 
< winx.h >

class  CHelloMainFrame :  public  winx::MainFrame < CHelloMainFrame >
{
    WINX_CLASS(
" CHelloMainFrame " );
public :
    
void  OnPaint(HWND hWnd)
    {
        winx::PaintDC dc(hWnd);
        dc.TextOut(
1 1 , _T( " Hello, WINX! " ));
    }
};

winx::CAppModule _Module;

int  APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     
int        nCmdShow)
{
    CAppModuleInit module;

    CHelloMainFrame::RegisterClass();
    CHelloMainFrame wndMain;
    wndMain.Create(NULL, _T(
" Hello " ));
    
    
return  module.Run();
}

 

WINX的编程模型

1. 注册窗口类(RegisterClass)

WINX中RegisterClass是需要主动调用的,这倒省了象ATL/WTL那样解释半天:-)

区别于已知的所有C++界面库(MFC、ATL/WTL、SmartWin、wxWidgets等等,甚至包括我早期写的SW系统),WINX倾向于把RegisterClass概念告诉用户。并且,为此我专门写了一篇“Windows精解:窗口类释疑”来解释相关概念的重要性。这一切与WINX的可视化策略有关,我们在“WINX如何做到可视化界面开发”中详述这一点。

以下这些宏与WINX的RegisterClass有关:

  • WINX_CLASS / WINX_CLASS_EX
  • WINX_CLASS_STYLE
  • WINX_DEFAULT_BKGND / WINX_DEFAULT_COLOR / WINX_DEFAULT_BRUSH
  • WINX_DEFAULT_CURSOR / WINX_DEFAULT_SYSCURSOR

它们分别对应Windows窗口类(WNDCLASSEX)中的成员:

  • lpszClassName
  • style
  • hbrBackground
  • hCursor

大家已经熟悉用WINX_CLASS指定窗口类的名称,其他宏的用法完全一致。例如,默认鼠标光标是箭头(IDC_ARROW),要改为象Edit控件一样使用IDC_IBEAM,很容易:

class  CHelloMainFrame :  public  winx::MainFrame < CHelloMainFrame >
{
    WINX_DEFAULT_SYSCURSOR(IDC_IBEAM);
    ...
};

 

2. 初始化类(InitClass)

WINX引入了许多小巧的初始化类。大致有:

  • CComAppInit - COM初始化类,即CoInitialize/CoUninitialize对。 
  • COleAppInit - OLE初始化类,即OleInitialize/OleUninitialize对。
  • CDebugAppInit - 启动内存泄漏调试(仅Debug版本,Release版本为空类)。
  • CComModuleInit - CComModule Init/Term。
  • CAppModuleInit - CAppModule Init/Term。
  • GdiplusAppInit - GdiplusStartup/GdiplusShutdown。

这些初始化类代码简单,但是抽象得恰到好处。在WINX之前,我曾经试图把这些初始化过程包装起来不让用户看到,但是最终不得不放弃。

 

3. 消息循环(MessageLoop)

目前,WINX并未提供自己的消息循环。我们借用WTL的CMessageLoop::Run。你没有在WINX的例子中见到CMessageLoop,是因为它被CAppModuleInit 类隐藏起来了。

class  CAppModuleInit :  public  WTL::CMessageLoop
{
public :
    CAppModuleInit(
        _ATL_OBJMAP_ENTRY
*  p  =  NULL,
        HINSTANCE hInst 
=  GetThisModule(),
        
const  GUID *  plibid  =  NULL)
    {
        _Module.Init(p, hInst, plibid);
        _Module.AddMessageLoop(
this );
    }
    
~ CAppModuleInit()
    {
        _Module.Term();
    }
};

 

4. 窗口过程(WindowProc)

消息循环中,消息最终被Windows发送到窗口过程(WindowProc)中。那么WINX的窗口过程在哪?

template  < class  WindowClass,  class  HandleClass  =  DefaultWindowHandle >
class  Window
{
public :
    
static  LRESULT CALLBACK WindowProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        WindowClass
*  pWnd  =  (WindowClass * )WindowMap::GetWindow(hWnd);
        
if  (pWnd  ==  NULL)
        {
            
if  (message  !=  WM_NCCREATE)
                
return  pWnd -> InternalDefault(hWnd, message, wParam, lParam);
            LPCREATESTRUCT lpCS 
=  (LPCREATESTRUCT)lParam;
            
if  (lpCS -> lpCreateParams) {
                pWnd 
=  (WindowClass * )lpCS -> lpCreateParams;
                lpCS
-> lpCreateParams  =  NULL;
            }
            
else  {
                
if  (WindowClass::StackWindowObject) {
                    WINX_ASSERT(
" WindowClass::StackWindowObject - unexpected! " );
                    
return  FALSE;
                }
                
else  {
                    pWnd 
=  WINX_NEW(WindowClass);
                }
            }
            WindowMap::SetWindow(hWnd, pWnd);
        }
        
return  pWnd -> ProcessMessage(hWnd, message, wParam, lParam);
    }
};

这里面有几个细节需要解释:

  • WindowMap::GetWindow/SetWindow是什么?在介绍“SW系统的窗口类”时,我们提到:
       - MFC通过一个全局的HashMap建立窗口句柄(hWnd)到窗口对象(pWnd)的映射。
       - SW系统通过窗口的UserData建立窗口句柄(hWnd)到窗口对象(pWnd)的映射。

    在WINX中,建立两者映射的策略是任选的。除了以上两者中外,还有第三种选择:
      - 使用SetProp/GetProp建立映射。并且这是WINX默认的选择。
      - 你自己实现的其他方式。

    我们简单分析一下,这些方式的利弊。
      - 通过HashMap建立映射,问题在于这个HashMap对象如何在其他的DLL中取到?这导致bug或者强耦合的结构。
      - 通过窗口的UserData建立映射,问题在于如果UserData已经被占用怎么办?这导致机制上不安全的隐患。
      - 使用SetProp/GetProp建立映射,性能比UserData方式慢,但极其安全。
     
  • WindowClass::StackWindowObject是什么?我们知道,对象(当然包括C++窗口对象)有两种创建方式:
      - 创建在栈(Stack)上。即以局部自动变量方式创建。
      - 创建在堆(Heap)上。即通过new/delete创建。
    WINX允许你为窗口类选择其中一种。详细我们在以后由专文叙述。
     
  • 最后,窗口消息被pWnd->ProcessMessage(hWnd, message, wParam, lParam)处理。ProcessMessage进行了最终的消息分派。这一块是WINX消息机制的核心,前面我们我们已经仔仔细细作了讲解:
      - WINX的消息分派机制
      - WINX的消息分派机制(续)
      - WINX的消息分派机制(续2)
      - WINX的消息分派机制(终结篇)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值