在C++类中实现Windows窗口的创建

//========================================================================
//TITLE:
//    在C++类中实现Windows窗口的创建
//AUTHOR:
//    norains
//DATE:
//    Thursday  9-November-2006
//========================================================================
  在面向过程的方法中实现窗口的创建很简单,但有个非常明显的缺点,就是封装不好。如果是自写自用,倒不是一个很大的问题,但如果是写给用户的,可能用户在包含头文件之后,看到那一大堆函数以及变量声明就已经晕掉。最好的方法当然是使用类,只给使用者留出应该使用的接口。但这会有个问题,就是消息处理函数必须为static的CALLBACK,否则编译会出错;但如果消息处理函数为static,其就归属于类,就根本无法调用对象的成员函数,而这个在经典的《windows 程序设计》中也没有涉及到。
  难道我们就只能束手无策,只能采用面向过程的方法了么?那倒未必,毕竟在MFC框架中已经做到,那么相信我们也可以做到。经过一番摸索,终于找到在类中创建窗口的方法,在此不敢独享,放出代码和大家探讨。
  
  为便于理解,代码的作用很简单,仅仅是完成了类的声明和对话框的创建,唯一多余的是在接收到WM_INITDIALOG消息时弹出一个消息框,以证明private函数能够正常调用。
  代码首先是声明一个CMainDlg类:
  ///////////////////////////
  //MainDlg.h
  ///////////////////////////
  class CMainDlg 
  {
   
   //--------------------------------------------------------------------------------------------   
   public:
    //静态的回调函数
    static BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
   
   //-------------------------------------------------------------------------------------------
   //Function
   public:
    //构造函数
    CMainDlg(HINSTANCE hInst);
    //析构函数
    virtual ~CMainDlg();
    //创建对话框
    BOOL CreateMainDlg();
    //消息调用函数,具体意义请参见函数的实现
    BOOL CallDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
   
   private:
    //临时测试调用函数,仅作为调试用
    void TempTestFuncion(){MessageBox(NULL,L"test",L"",NULL);};
   
   //---------------------------------------------------------------------------------------------
   //数据成员
   private:
    HINSTANCE m_hInst;
    HWND m_hDlg;
   
  };
  
  
  
  然后是MainDlg类的实现:
  /////////////////////////////////
  //MainDlg.cpp
  ////////////////////////////////
                                                                                     
    #include "stdafx.h"                                                                
    #include "MainDlg.h"                                                               
    #include "resource.h"                                                              
                                                                                   
    //全局指针,用来指向所创建的对象,即this的指向对象                                                              
    CMainDlg *g_pDlg = NULL;                         
                 
    //消息的回调函数的实现                                                  
    BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {                                                                                  
     if(g_pDlg != NULL)                                                               
     {
      //如果this指针不为空,则调用CallDlgProc函数                                                                               
      return g_pDlg->CallDlgProc(hWnd,wMsg,wParam,lParam);                           
     }                                                                                
     return FALSE;                                                                    
    }                                                                                  
   
    //构造函数                                                                               
    CMainDlg::CMainDlg(HINSTANCE hInst)                                                
    {                                                                                  
     g_pDlg = this; //存储this对象到全局变量中                                                                   
     m_hInst = hInst;                                                                 
    }                                                                                  
   
    //析构函数                                                                               
    CMainDlg::~CMainDlg()                                                              
    {                                                                                  
                                                                                   
    }                                                                                  
       
    //创建对话框                                                                           
    BOOL CMainDlg::CreateMainDlg()                                                     
    {                                                                                  
     m_hDlg = CreateDialog(m_hInst,MAKEINTRESOURCE(IDD_MAIN_DLG),NULL,MainDlgProc);   
     if(m_hDlg == NULL)                                                               
     {                                                                                
      return FALSE;                                                                  
     }                                                                                
     ShowWindow(m_hDlg,TRUE);//显示窗口                                                         
                                                                                      
     return TRUE;                                                                     
    }                                                                                  
        
    //真正的消息处理函数,在这里可以任意调用对象的成员函数                                                                          
    BOOL CMainDlg::CallDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)        
    {                                                                                  
     switch(wMsg)                                                                     
     {                                                                                
      case WM_INITDIALOG:                                                            
       TempTestFuncion(); //norains:测试的私有函数                                      
       break;                                                                       
     }                                                                                
     return FALSE;                                                                    
    }                          
   
   
   
    这是在主程序中对类的的调用:   
    //////////////////
    //MainApp.cpp
    //////////////////
    #include "stdafx.h"
    #include "MainDlg.h"
  
  int WINAPI WinMain( HINSTANCE hInstance,
       HINSTANCE hPrevInstance,
       LPTSTR    lpCmdLine,
       int       nCmdShow)
  {
   //声明一个对象 
   CMainDlg mainDlg(hInstance);
   //创建并显示窗口
   if(mainDlg.CreateMainDlg() == FALSE)
   {
    return 0x05;
   }
   
   //消息循环
   MSG msg;
   while(GetMessage(&msg,NULL,0,0))
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
   return 0;
  }
  
  这段代码的关键点在于MainDlgProc声明为类的static函数,以及使用g_pDlg来存储this对象指针,并在MainDlgProc中调用。只要注意到这两点,在类中创建一个窗口就不是一件非常困难的事情。
  
  对于CallDlgProc()函数,可能有不少人觉得是鸡肋,因为相同的功能完全可以在MainDlgProc()中实现,比如这程序的代码完全可以撇开CallDlgProc()写成这样:
  
  BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {                                                                                  
     if(g_pDlg != NULL)                                                               
     {
     switch(wMsg)                                                                     
      {                                                                                
       case WM_INITDIALOG:                                                            
        g_pDlg->TempTestFuncion();                                       
        break;                                                                       
      }                                 
     }                                                                                
     return FALSE;                                                                    
    }               
    所完成的功能和之前的代码相同。不过如果要更改之后的代码正常工作的话,还需要把TempTestFuncion()函数声明为public。也许有的读者已经发现问题之所在:如果对消息相应的处理函数不多,这样倒是可以;但如果很多的话,那么那些消息相应函数都需要声明为public,并且这些消息响应函数都是用户不可能用到的!用户看到如此之多用不到的public函数,即使没头晕,也需要好一阵子才能回神过来。
    所以呢,CallDlgProc()函数只是起到一个缓冲作用,不必要把所有的消息相应函数都暴露给用户。虽然这样做也暴露了CallDlgProc()函数,但毕竟相对于上十个甚至上百个消息响应函数而言,仅仅只是暴露一个没什么作用的CallDlgProc()函数函数,应该能让用户减轻不少负担才是。               
   
    在网上看到有一个方法不是采用全局变量,而是直接设置窗口数据,然后再获取。这方法比较有意思,在此将更改的代码贴出:           
    BOOL CMainDlg::CreateMainDlg()                                                     
    {                                                                                  
     m_hDlg = CreateDialog(m_hInst,MAKEINTRESOURCE(IDD_MAIN_DLG),NULL,MainDlgProc);   
     if(m_hDlg == NULL)                                                               
     {                                                                                
      return FALSE;                                                                  
     }                                                                                
     ShowWindow(m_hDlg,TRUE);//显示窗口                                                      
     SetWindowLong(m_hDlg,GWL_USERDATA,(LONG)this); //将this指针传递给窗口
                                                                                  
     return TRUE;                                                                     
    }   
   
    BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {   
     static CMainDlg * pDlg = NULL;
   if(pDlg == NULL)
   {
    pDlg = (CMainDlg *)GetWindowLong(hWnd,GWL_USERDATA);
   } 
                                                                              
     if(pDlg != NULL)                                                               
     {
     switch(wMsg)                                                                     
      {                                                                                
       case WM_INITDIALOG:                                                            
        pDlg->TempTestFuncion();                                       
        break;                                                                       
      }                                 
     }                                                                                
     return FALSE;                                                                    
    }  
    这段代码关键就只在于两个函数:SetWindowLong()和GetWindowLong().很巧妙地让窗口通过这两个函数来传输this对象,的确是一个很好的方法.

posted @ 2006-11-09 23:56  我的一天  阅读(1326)  评论(0编辑  收藏  举报