zoukankan      html  css  js  c++  java
  • 窗口子类化实例应用

    所谓窗口子类化:改变一个已经存在的窗口实例的性质:消息处理与其他实例属性。

     

    通常在SDK中所谓的窗口子类化就是改变一个窗口函数(如GetWindowLong()SetWindowLong())通过这两个函数来设置窗口的属性等;

     

    而今天我们主要内容是介绍MFC中的子类化,它跟SDK中的子类化不太一样:

    所有MFC窗口有相同的窗口函数,由该窗口函数根据窗口句柄查找窗口实例,在把消息映射到该窗口类(class)得消息处理函数上。为了利用MFC的消息映射机制,不宜改变窗口函数(名),MFC也把子类化封装在函数SubclassWindow()中。

     

    #include "Subclass.h"

    class  CSubclassWnd : public CObject {

    public:

           DECLARE_DYNAMIC(CSubclassWnd);

           CSubclassWnd();

           ~CSubclassWnd();

     

           // Subclass a window. Hook(NULL) to unhook (automatic on WM_NCDESTROY)

           virtual BOOL       HookWindow(HWND  hwnd);

           virtual BOOL       HookWindow(CWnd* pWnd)   { return HookWindow(pWnd->GetSafeHwnd()); }

           virtual BOOL       IsHooked()                                { return m_hWnd!=NULL; }

     

           friend LRESULT CALLBACK HookWndProc(HWND, UINT, WPARAM, LPARAM);

           friend class CSubclassWndMap;

     

    #ifdef _DEBUG

           virtual void AssertValid() const;

           virtual void Dump(CDumpContext& dc) const;

    #endif

     

    protected:

           HWND                        m_hWnd;                           // the window hooked

           WNDPROC                 m_pOldWndProc;              // ..and original window proc

           CSubclassWnd*    m_pNext;                           // next in chain of hooks for this window

     

           // Override this to handle messages in specific handlers

           virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);

           virtual LRESULT Default();                           // call this at the end of handler fns

    };

     

    #include "Subclass.cpp"

    CSubclassWnd::CSubclassWnd()

    {

           m_pNext = NULL;

           m_pOldWndProc = NULL;

           m_hWnd  = NULL;

    }

     

    CSubclassWnd::~CSubclassWnd()

    {

           if (m_hWnd)

                  HookWindow((HWND)NULL);        // unhook window

    }

     

    //////////////////

    // Hook a window.

    // This installs a new window proc that directs messages to the CSubclassWnd.

    // pWnd=NULL to remove.

    //

    BOOL CSubclassWnd::HookWindow(HWND hwnd)

    {

           ASSERT_VALID(this);

           if (hwnd) {

                  // Hook the window

                  ASSERT(m_hWnd==NULL);

                  ASSERT(::IsWindow(hwnd));

                  theHookMap.Add(hwnd, this);                // Add to map of hooks

     

           } else if (m_hWnd) {

                  // Unhook the window

                  theHookMap.Remove(this);                            // Remove from map

                  m_pOldWndProc = NULL;

           }

           m_hWnd = hwnd;

           return TRUE;

    }

     

    //////////////////

    // Window proc-like virtual function which specific CSubclassWnds will

    // override to do stuff. Default passes the message to the next hook;

    // the last hook passes the message to the original window.

    // You MUST call this at the end of your WindowProc if you want the real

    // window to get the message. This is just like CWnd::WindowProc, except that

    // a CSubclassWnd is not a window.

    //

    LRESULT CSubclassWnd::WindowProc(UINT msg, WPARAM wp, LPARAM lp)

    {

    //     ASSERT_VALID(this);  // removed for speed

           ASSERT(m_pOldWndProc);

           return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :    

                  ::CallWindowProc(m_pOldWndProc, m_hWnd, msg, wp, lp);

    }

     

    //////////////////

    // Like calling base class WindowProc, but with no args, so individual

    // message handlers can do the default thing. Like CWnd::Default

    //

    LRESULT CSubclassWnd::Default()

    {

           // MFC stores current MSG in thread state

           MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;

           // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinte

           // recursion on virtual function

           return CSubclassWnd::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);

    }

     

    #ifdef _DEBUG

    void CSubclassWnd::AssertValid() const

    {

           CObject::AssertValid();

           ASSERT(m_hWnd==NULL || ::IsWindow(m_hWnd));

           if (m_hWnd) {

                  for (CSubclassWnd* p = theHookMap.Lookup(m_hWnd); p; p=p->m_pNext) {

                         if (p==this)

                                break;

                  }

                  ASSERT(p); // should have found it!

           }

    }

     

    void CSubclassWnd::Dump(CDumpContext& dc) const

    {

           CObject::Dump(dc);

    }

     

    #endif

     

    //////////////////

    // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever

    // else was there before.)

    //

    LRESULT CALLBACK

    HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)

    {

    #ifdef _USRDLL

           // If this is a DLL, need to set up MFC state

           AFX_MANAGE_STATE(AfxGetStaticModuleState());

    #endif

     

           // Set up MFC message state just in case anyone wants it

           // This is just like AfxCallWindowProc, but we can't use that because

           // a CSubclassWnd is not a CWnd.

           //

           MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;

           MSG  oldMsg = curMsg;   // save for nesting

           curMsg.hwnd              = hwnd;

           curMsg.message = msg;

           curMsg.wParam  = wp;

           curMsg.lParam  = lp;

     

           // Get hook object for this window. Get from hook map

           CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);

           ASSERT(pSubclassWnd);

     

           LRESULT lr;

           if (msg==WM_NCDESTROY) {

                  // Window is being destroyed: unhook all hooks (for this window)

                  // and pass msg to orginal window proc

                  //

                  WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;

                  theHookMap.RemoveAll(hwnd);

                  lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);

     

           } else {

                  // pass to msg hook

                  lr = pSubclassWnd->WindowProc(msg, wp, lp);

           }

           curMsg = oldMsg;               // pop state

           return lr;

    }

     

    这边只是介绍SubclassWindow() 内部实现方式。

     

    CSubclassWnd类的作用是不用派生新类便能在MFC中子类化窗口。

     

    现在我们这边只提CSubclass Wnd 类的一个作用,其实CSubclass Wnd 也经常 被说成是万能类,可以用在很多的界面和控件上。

     

     

     

    具体实例:

    以美图秀秀的弹出对话框自定义皮肤为例:

    创建一个自定义皮肤类:
    class CDlgSkin : public CSubclassWnd

    {

           public:

                  自定义变量

           CDlgSkin();

           CDlgSkin(LPCTSTR skinfile)

    {

           CDlgSkin();

           LoadSkin(skinfile);

    }

    Virtual ~CDlgSkin();

     

    BOOL InitSkinWin(CString strPath,CWnd *wnd);

    virtual LRESULT WindowProc(UINT msg, WPARAM wparam,LPARAM lparam);

           ……

           ……

     

    }

    //初始化

    BOOL CDlgSkinWin::InstallSkin(CWnd *wnd)

    {

           if(!wnd || !m_bInit)

                  return FALSE;

    //开始捕获此窗口的消息

    //     HookWindow((HWND)NULL);

           int r = HookWindow(wnd);

     

           return r;

    }

     

    //消息处理函数

    LRESULT CDlgSkin::WindowProc(UINT msg, WPARAM wp/*wparam*/,LPARAM lp/*lparam*/)

    {

           if ( !IsWindow(m_hWnd) )

                  return 0;

           if ( !m_bInit  )

                  return Default();

          

           switch ( msg )

           {

           case WM_SHOWWINDOW:

                  //call setwindowpos to force OnNcCalcSize when hWnd is a dialog

                  if ( wp )

                         SetWindowPos( m_hWnd, 0, 0, 0, 400, 400, SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED );

                  Default();

                  m_downHitTest = 0;

                  m_moveHitTest = 0;

                  return 0;

                  break;

                  //case WM_ERASEBKGND:

           case WM_INITMENUPOPUP:

                  Default();

                  return 0;

           case WM_SYSCOMMAND:

                  OnSysCommand( wp, lp );

                  return 0;

           case WM_SETTEXT:

            return OnSetText( wp, lp );

           case WM_NCPAINT:

                  OnNcPaint( (HRGN)wp );

                  return 0;

           case WM_NCCALCSIZE:

                  OnNcCalcSize( (BOOL)wp, (NCCALCSIZE_PARAMS *)lp );

                  return 0;

           case WM_SIZE:

                  OnSize( wp, LOWORD(lp), HIWORD(lp) );

                  return 0; 

           case WM_NCACTIVATE:

                  return OnNcActivate( (BOOL)wp );

           case WM_NCHITTEST:

                  return OnNcHitTest(CPoint(LOWORD(lp), HIWORD(lp)));

           case WM_NCLBUTTONUP:

                  OnNcLButtonUp(wp, CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_NCLBUTTONDOWN:

                  OnNcLButtonDown(wp, CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_NCLBUTTONDBLCLK:

                  OnNcLButtonDblClk(wp, CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_NCRBUTTONUP:

                  OnNcRButtonUp(wp, CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_NCRBUTTONDOWN:

                  OnNcRButtonDown(wp, CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_NCMOUSEMOVE:

                  OnNcMouseMove( wp,CPoint(LOWORD(lp), HIWORD(lp)));

                  return 0;

           case WM_GETMINMAXINFO:

                  OnGetMinMaxInfo( (MINMAXINFO *)lp );

                  return 0;

           case WM_WINDOWPOSCHANGING:

                  OnWindowPosChanging((WINDOWPOS *)lp);

                  return 0;

           case WM_SIZING:

                  OnSizing( wp, (LPRECT)lp );

                  return 0;

           case WM_ACTIVATE:

                  OnActivate( wp, CWnd::FromHandle((HWND)lp), 0 );

                  return 0;

           case WM_COMMAND:

                  if ( !HandleSysCommand( wp, lp ) )

                         Default();

                  return 0;

     

           default:

                  return Default();

           }

     

    }

     

     

    简单的调用方法:

    Class  B public A 

      ……
    }
    A  a; 
    B  b; 
    HWND ha=a.GetSafeHwnd();
    b.SubclassWindow(ha); #
    当然不一定是继承关系。
    注意:在被子类化的窗口销毁之前,必须执行窗口的反子类化: 
    b.UnSubclassWindow();

     

    子类化跟超类化的区别:

    超类化:首先获得一个已存在的窗口类,然后设置窗口类,最后注册该窗口类。

    如:

    WNDCLASSEX  wc; 
    wc.cbSize=sizeof(wc); //Windows
    用来进行版本检查的,与窗口特征无关 
    GetClassInfoEx(hinst,”XXXXXX”,&wc);
     // hinst—
    定义窗口类XXXXXX的模块的句柄,如为系统定义的窗口类(如:EDITBUTTON)则hinst=NULL. 
    wc.lpszClassName = “YYYYYYY”;//
    必须改变窗口类的名字 
    wc.hbrBackGround = CreateSolidBrush(RGB(0,0.0));//
    改变背景刷 
    wc.lpfnWndProc = NewWndProc;//
    改变窗口函数 
    ……
    RegisterClassEx(&wc);// 
    注册新窗口类 
    //
    使用窗口类 
    ……
    ::CreateWindow(_T(“YYYYYYYY”,……)

    所以超类化只能改变自己创建的窗口特征,而不能用于windows创建的窗口;

    而子类化是实例级别上的,只要能获得窗口的句柄就可以对其子类化,这也是子类化对超类化的一个优势。

    总结

    0 子类化修改窗口过程函数,  超类化修改窗口类(新的窗口类名)
    1 子类化是在窗口实例级别上的,超类化是在窗口类(WNDCLASS)级别上的。 
    2 超类化可以完成比子类化更复杂的功能,在SDK范畴上,可以认为子类化是超类化的子集。 
    3 子类化只能改变窗口创建后的性质,对于窗口创建期间无能为力(无法截获ON_CREATE 事件),而超类化可以实现;超类化不能用于Windows创建的窗口,子类化可以。

    from:http://blog.csdn.net/lin_angle/article/details/6178351 

  • 相关阅读:
    让你的网站(MAXCMS4_0)按地区、年份、语言生成分页面(已经修正)
    IPTV
    超简单,MAX普通版改为资源版方法
    Jmter操作数据库
    JMter中添加断点和关联
    jmeter返回报文乱码问题
    Jmter安装和配置
    JMter压力测试
    今天注册了
    不能登陆后删除Cookies解决
  • 原文地址:https://www.cnblogs.com/lidabo/p/2975281.html
Copyright © 2011-2022 走看看