窗口子类化-实例应用

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

 

通常在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 

上一篇:nginx搭建静态资源web服务器(二)


下一篇:CentOS 5.3 cacti 完美安装配置