拦截消息是可以修改控件或者窗口现有的功能,MFC的子类化技术用C++语言把Windows窗口和各种控件镜像包装,是MFC的核心技术。
以文本框为例,使其只能输入数字。核心原理是一个文本框就是一个窗口,而每一个窗口都有自己的窗口过程。
主要会使用到一下三个函数:
- SetWindowLong()
SetWindowLong是一个WindowsAPI函数。该函数用来改变指定窗口的属性.函数也将指定的一个32位值设置在窗口的额外存储空间的指定偏移位置。 - CallWindowProc()
CallWindowProc是将消息信息传送给指定的窗口过程的函数。使用函数CallWindowsProc可进行窗口子分类。通常来说,同一类的所有窗口共享一个窗口过程。子类是一个窗口或者相同类的一套窗口,在其消息被传送到该类的窗口过程之前,这些消息是由另一个窗口过程进行解释和处理的
SetWindowLong函数通过改变与特定窗口相关的窗口过程,使系统调用新的窗口过程来创建子类,新的窗口过程替换了以前的窗口过程。应用程序必须通过调用CallWindowsProc来将新窗口过程没有处理的任何消息传送到以前的窗口过程中,这样就允许应用程序创建一系列窗口过程。 - SetProp的作用是让系统给你的窗口额外分配一定的空间,用来存储一些你自己定义的数据。打个比方,这就好比是银行的保险箱(Windows显然比银行大方得多,这个是不收费的),第一个参数HWND
hWnd指定了在哪家银行,第二个参数LPCSTR lpString指定了是哪个保险箱,至于第三个参数HANDLE
hData则是你要存放的东西。调用SetProp之后,系统就把你提交的那个hData帮你保存起来了,如果某个时候你想要用了,就用GetProp再取出来。如同银行一样,系统是不会管你交给他保存的是什么东西、有什么用处的,它只限定你交给它的东西的体积不能超过保险箱的尺寸,至于怎么使用是你自己的事情
以下示例:
1、首先在输入框中拖入文本编辑控件
2、添加一个新的类,这个类是用来实现控件功能的类
在这个类的头文件中添加一下函数:
#pragma once
class CDloubleEdit
{
public:
CDloubleEdit();
~CDloubleEdit();
BOOL Attach(HWND hWnd); //子类化的嫁接函数,用来将控件的句柄和这个类对象连接起来
void Detch();//子类化的释放函数,在关闭窗口或者析构的时候,释放句柄
BOOL GetStringValue(LPTSTR lpBuffer,int nLen);//获取文本控件的输入字符
BOOL GetDoubleValue(double *pValue);//
protected:
static LRESULT APIENTRY NewEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
//这个函数是在使用setWindowsLong函数的时候,新建的控件过程。消息首先会通过此函数。
protected:
HWND m_hWnd;//用来接收控件句柄
long m_lOldProc;//永磊接收上一个过程
};
3、在新建类的.cpp函数中实现功能。
(1)在嫁接函数中使用SetWindoslong()函数创建新的过程。
(2)NewEditProc实现函数新增的功能
#include "stdafx.h"
#include "DloubleEdit.h"
#define DOUBLE_EDIT_PROP_NAME _T("Double_EDit_Prop_Name")
CDloubleEdit::CDloubleEdit()
{
}
CDloubleEdit::~CDloubleEdit()
{
Detch();
}
BOOL CDloubleEdit::Attach(HWND hWnd)
{
if (m_hWnd != NULL)//判断句柄是不是为空
{
DebugBreak();
return false;
}
m_hWnd = hWnd;
//SetProp:该函数在指定窗口的属性表中增加一个新项,
//或者修改一个现有项。如果指定的字符串不在属性表中,那么就增加该新的项,
//新项中包含该字符串和句柄,否则就用指定的句柄替换该字符串的全前句柄。
SetProp(hWnd,DOUBLE_EDIT_PROP_NAME,this);//用一个字符串通过SetProp函数,将this(本类)的对象以属性的范式保存
m_lOldProc = SetWindowLong(hWnd,GWL_WNDPROC,(long)NewEditProc);//创建新的过程
if (m_lOldProc == 0)
{
m_lOldProc = NULL;
return false;
}
else
return TRUE;
}
LRESULT APIENTRY CDloubleEdit::NewEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDloubleEdit* pEdit = (CDloubleEdit*)GetProp(hWnd,DOUBLE_EDIT_PROP_NAME);
if (pEdit == NULL)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
BOOL bCanceled = FALSE;
if (uMsg == WM_CHAR)
{
TCHAR szBuf[32] = { 0 };
pEdit->GetStringValue(szBuf, 32);
switch (wParam)
{
case '.':
if (_tcschr(szBuf,'.'))
{
bCanceled = TRUE;
}
break;
case '_':
if (_tcschr(szBuf, '_'))
{
bCanceled = TRUE;
}
else if (LOWORD(SendMessage(hWnd, EM_GETSEL, 0, 0)) != 0)
{
bCanceled = FALSE;
}
break;
default:
if (wParam>'9' || wParam <'0')
{
bCanceled = TRUE;
}
break;
}
if (bCanceled)
{
MessageBeep(-1);
return 0;
}
}
return CallWindowProc((WNDPROC)pEdit->m_lOldProc, hWnd, uMsg, wParam, lParam);
}
BOOL CDloubleEdit::GetStringValue(LPTSTR lpBuffer, int nLen)
{
return GetWindowText(m_hWnd,lpBuffer,nLen);
}
BOOL CDloubleEdit::GetDoubleValue(double *pValue)
{
TCHAR szBuf[32];
if (GetStringValue(szBuf, 32))
{
*pValue = atof((const char*)szBuf);
return TRUE;
}
else
return FALSE;
}
void CDloubleEdit::Detch()
{
if (m_hWnd == NULL)
{
return;
}
SetWindowLong(m_hWnd, GWL_WNDPROC, m_lOldProc);
m_hWnd = NULL;
m_lOldProc = 0;
}
4、在对话框的实现类中添加新建类的对象,调用新建类的attch函数嫁接文本编辑框的句柄。
CDloubleEdit g_edit;
g_edit.Attach(GetDlgItem(IDC_EDI_INPUT)->GetSafeHwnd());