消息处理函数的转移

//========================================================================
//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: 

    SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWORD)CtrlProc);

    就这么简单,现在只要m_hEdWord窗口收到消息,那么就会自动调用预先定义的CtrlProc.
 
    有设置,自然也有获取,不过这次我们是通过GetWindowLong函数:
    m_pPreProcEdWord  =  (WNDPROC)GetWindowLong(m_hEdWord,GWL_WNDPROC);

   现在m_pPreProcEdWord存储的就是目前m_hEdWord的消息处理函数地址.不过,其实通过SetWindowLong函数也能获取消息函数地址:
    m_pPreProcEdWord  =  SetWindowLong(m_hEdWord,GWL_WNDPROC,(DWORD)CtrlProc);

    不过这时候获取的却是在设置CtrlProc消息处理函数之前的函数地址.也就是说,这两段代码中m_pPreProcEdWord等价:
     1 ).
    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:
    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);

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地址存储了当前对象指针:

    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 ;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
函数指针是一个指向函数的地址的指针。在C语言中,函数指针常用于回调函数和转移表。 一、回调函数 回调函数是一种通过函数指针来实现的函数调用方式,它将一个函数的指针作为参数传递给另一个函数,使得被调用函数可以在需要的时候调用这个指针所指向的函数。回调函数通常用于事件处理、异步调用和扩展程序等场景。下面是一个简单的示例代码: ```C #include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } void calc(int (*func)(int, int), int a, int b) { int result = func(a, b); printf("result: %d\n", result); } int main() { calc(add, 2, 3); // 输出:result: 5 calc(sub, 5, 2); // 输出:result: 3 return 0; } ``` 在上面的代码中,calc函数接受一个函数指针和两个整数参数,它将传入的函数指针所指向的函数作为参数调用,并将返回值输出到控制台。 二、转移转移表是一种将函数指针数组作为参数的编程技巧,它通常用于实现状态机、多路分发等场景。转移表可以将复杂的条件判断简化为数组索引,从而提高程序的可读性和可维护性。下面是一个简单的状态机示例代码: ```C #include <stdio.h> typedef enum { STATE_IDLE, STATE_READY, STATE_RUNNING, STATE_STOPPED } State; void idle() { printf("IDLE\n"); } void ready() { printf("READY\n"); } void running() { printf("RUNNING\n"); } void stopped() { printf("STOPPED\n"); } void (*state_table[])(void) = { idle, ready, running, stopped }; int main() { State state = STATE_IDLE; while (1) { state_table[state](); // 状态转移 switch (state) { case STATE_IDLE: state = STATE_READY; break; case STATE_READY: state = STATE_RUNNING; break; case STATE_RUNNING: state = STATE_STOPPED; break; case STATE_STOPPED: state = STATE_IDLE; break; default: break; } } return 0; } ``` 在上面的代码中,state_table是一个函数指针数组,它包含了所有状态对应的函数指针。程序通过循环遍历state_table数组,并根据当前状态调用相应的函数。在状态转移时,使用switch语句实现简单的状态机逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值