//========================================================================
//TITLE:
// 消息处理函数的转移
//AUTHOR:
// norains
//DATE:
// Wednesday 03-January-2008
//Environment:
// VS2005 + SDK-WINCE5.0-MIPSII
// EVC + SDK-WINCE5.0-MIPSII
//========================================================================
Windows CE有一个很有意思的API函数,通过SetWindowLong函数可以转移原窗口的消息处理函数为自定义的.敏感的朋友估计一看见,就已经明白可以做什么了.呵呵,难道不是么?
1.函数使用
SetWindowLong的使用及其简单,比如我们将m_hEdWord窗口的消息处理函数置换为CtrlProc:
就这么简单,现在只要m_hEdWord窗口收到消息,那么就会自动调用预先定义的CtrlProc.
有设置,自然也有获取,不过这次我们是通过GetWindowLong函数:
现在m_pPreProcEdWord存储的就是目前m_hEdWord的消息处理函数地址.不过,其实通过SetWindowLong函数也能获取消息函数地址:
不过这时候获取的却是在设置CtrlProc消息处理函数之前的函数地址.也就是说,这两段代码中m_pPreProcEdWord等价:
m_pPreProcEdWord = SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWORD)CtrlProc);
2 ).
m_pPreProcEdWord = (WNDPROC)GetWindowLong(m_hEdWord,GWL_WNDPROC);
SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWORD)CtrlProc);
如果需要调用m_pPreProcEdWord指向的函数,则需要用上CallWindowProc:
2.接管Windows Control消息
说了一大堆似乎很有哲理的话,现在就让我们来看看有什么实际的作用吧.
估计这个函数用得最多是在Windows Control中.比如,你想在Edit Box输入某个字符时做一些特殊处理,只能通过SetWindowLong来置换内部的处理函数进而调用自己的特殊处理函数.
我们来举一个非常简单的例子,首先创建一个Edit Box控件,如果在该控件中按下键盘的"ESC",则会退出应用程序.为了方便突出问题的重点,我们的CMainWnd窗口继承于CWndBase(关于CWndBase请见:http://blog.youkuaiyun.com/norains/archive/2007/11/10/1878218.aspx)
#pragma once
#include " wndbase.h "
class CMainWnd :
public CWndBase
{
public :
CMainWnd( void );
~ CMainWnd( void );
BOOL Create(HINSTANCE hInst, HWND hWndParent, const TCHAR * pcszWndClass, const TCHAR * pcszWndName);
protected :
static LRESULT CtrlProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
private :
HWND m_hEdWord; // The window is for inputing the word
WNDPROC m_pPreProcEdWord; // Pointer to the previous window procedure for the input word window.
};
#include " stdafx.h "
#include " MainWnd.h "
// ---------------------------------------------------------------------------------------
// Default value
#define IDC_EDIT_WORD 101
/ /
// Construction/Destruction
/ /
CMainWnd::CMainWnd( void )
{
}
CMainWnd:: ~ CMainWnd( void )
{
}
// ----------------------------------------------------------------------
// Description:
// Create the window. It's override function
//
// ----------------------------------------------------------------------
BOOL CMainWnd::Create(HINSTANCE hInst, HWND hWndParent, const TCHAR * pcszWndClass, const TCHAR * pcszWndName)
{
if (CWndBase::Create(hInst, hWndParent, pcszWndClass, pcszWndName) == FALSE)
{
return FALSE;
}
// The edit window for input the word
m_hEdWord = CreateWindowEx(WS_EX_TOPMOST,
TEXT( " EDIT " ),
TEXT( "" ),
ES_LEFT | WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_BORDER,
GetSystemMetrics(SM_CXSCREEN) / 3 ,
GetSystemMetrics(SM_CYSCREEN) / 3 ,
GetSystemMetrics(SM_CXSCREEN) / 3 ,
GetSystemMetrics(SM_CYSCREEN) / 3 ,
m_hWnd,
(HMENU)IDC_EDIT_WORD,
m_hInst,
NULL);
// Store the pointer in the window
SetWindowLong(m_hEdWord, GWL_USERDATA, (DWORD) this );
// Get the previous window procedure
m_pPreProcEdWord = (WNDPROC)GetWindowLong(m_hEdWord,GWL_WNDPROC);
// Set the new window procedure
SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWORD)CtrlProc);
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Windows control process.
//
// ----------------------------------------------------------------------
LRESULT CMainWnd::CtrlProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
CMainWnd * pObject = (CMainWnd * )GetWindowLong(hWnd,GWL_USERDATA);
if (pObject -> m_hEdWord == hWnd)
{
switch (wMsg)
{
case WM_CHAR:
{
if ((TCHAR) wParam == VK_ESCAPE)
{
PostQuitMessage( 0x00 );
}
break ;
}
}
return CallWindowProc(pObject -> m_pPreProcEdWord,hWnd,wMsg,wParam,lParam);
}
// It should never get here !
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
程序代码段很短,但有几点需要注意的地方.
CtrlProc是我们用来置换原有过程的消息处理函数,因为CtrlProc为静态才能回调,而静态函数无法调用成员变量,所以我们在创建Edit Box实例之后在GWL_USERDATA地址存储了当前对象指针:
然后在CtrlProc函数中获取存储在GWL_USERDATA中的对象指针,用该指针调用成员变量及函数:
因为我们只是处理WM_CHAR消息,其它消息都采用默认处理方式,所以直接返回调用先前的消息函数:
这个例子很简单,该工程可以在此下载:http://download.youkuaiyun.com/source/324833
3.系统必崩溃代码
如果将消息处理函数处理转移到已经释放的内存上,那么会有什么结果呢?结果就是,系统崩溃!有兴趣的朋友可以试试这段代码:
{
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
HWND hWnd = GetForegroundWindow();
SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)MyProc);
return 0 ;
}