//========================================================================
//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:
GWL_WNDPROC);
SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWord)CtrlProc);
如果需要調用m_pPreProcEdWord指向的函數,則需要用上CallWindowProc:
CallWindowProc(m_pPreProcEdWord,hWnd,wMsg,wParam,lParam);
2.接管Windows Control消息
說了一大堆似乎很有哲理的話,現在就讓我們來看看有什麼實際的作用吧.
估計這個函數用得最多是在Windows Control中.比如,你想在Edit Box輸入某個字符時做一些特殊處理,只能通過SetWindowLong來置換內部的處理函數進而調用自己的特殊處理函數.
我們來舉一個非常簡單的例子,首先創建一個Edit Box控件,如果在該控件中按下鍵盤的"ESC",則會退出應用程序.為了方便突出問題的重點,我們的CMainWnd窗口繼承於CWndBase(關於CWnDBase請見:http://blog.csdn.Net/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);
LOR: #808080">//////////////////////////////////////////////////////////////////////
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 n>= (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地址存儲了當前對象指針:
SetWindowLong(m_hEdWord, GWL_USERDATA, (DWord)this);
然後在CtrlProc函數中獲取存儲在GWL_USERDATA中的對象指針,
用該指針調用成員變量及函數:
CMainWnd *pObject = (CMainWnd *)GetWindowLong(hWnd,GWL_USERDATA);
因為我們只是處理WM_CHAR消息,其它消息都采用默認處理方式,所以直接返回調用先前的消息函數:
return CallWindowProc(pObject->m_pPreProcEdWord,hWnd,wMsg,wParam,lParam);
這個例子很簡單,該工程可以在此下載:http://download.csdn.Net/source/324833
3.系統必崩潰代碼
如果將消息處理函數處理轉移到已經釋放的內存上,那麼會有什麼結果呢?結果就是,系統崩潰!有興趣的朋友可以試試這段代碼:
LRESULT MyProc(HWND hWnd, UINT wMsg,WPARAM wParam, LPARAM lParam)
{
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;
}