windows程序设计之窗口子类化

这几天都在家里,赶上了暑假,没啥事情可做,就做些小玩意。虽然都没什么技术含量,但自己毕竟是新手,做得不是很好,代码都得参考下别人的。委屈今天看到一个博客上面写的是关于SDK的窗口子类化,什么是窗口子类化,其实说得简单点就是有个多功能的窗口,你对它有绝对的控制权得意。举个例子好了,你自己写了一个软件,上面有个编辑框,在这里面只能输入“我是笨蛋”,(基本上没人会喜欢你的软件)客户要是想输入其他的,都输不进去。这就有一种思想,软件是我做的,我对它有绝对的控制权。

下面简单描述下原理:就拿上面那个例子来说好了,我前面的博客大概介绍了下windows是怎么处理键盘消息的。就拿‘A’键来说,你要是不处理,windows就会自己拜托DefWindowProc 自己去处理。就算你要处理了,那好,在WM_CHAR或者WM_KEYDOWN你自己处理吧,但其实这不是享有绝对的控制权。

真正要有绝对的控制权是我需要具有输入检测的能力,即每当用户输入一个字符到编辑框中时要能检测这个字符。那要怎么办呢?可以这样,把Windows的窗口过程处理函数“偷换”成自己的函数,这样你就能把所有的消息随你高兴怎么处理了,这才是真正有有绝对控制权得意,也就是所谓的窗口子类化。由于Windows是认定窗口过程函数的格式的,所以你自己定义的函数也要和Windows本事的窗口过程函数格式一样。这样一说其实也就很简单了。

窗口子类化之前

Windows< ==>Edit 控件的窗口处理函数。

子类化之后
Windows< ==>自定义的窗口处理函数==> Edit 控件的窗口处理函数。

注意一点子类化并不局限于控件,可以子类化任何窗口

还是先看代码吧:(代码主要实现功能:在编辑框中只能输入0或者1,可用于纯二进制输入)

#include <windows.h>

#define  ID_EDIT  1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK EditWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ;

WNDPROC OldWndProc;
static HWND  hwndEdit; 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
					PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT ("窗口子类化") ;
	HWND         hwnd ;
	MSG          msg ;
	WNDCLASS     wndclass ;

	wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc   = WndProc ;
	wndclass.cbClsExtra    = 0 ;
	wndclass.cbWndExtra    = 0 ;
	wndclass.hInstance     = hInstance ;
	wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
	wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
	wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
	wndclass.lpszMenuName  = NULL ;
	wndclass.lpszClassName = szAppName ;

	if (!RegisterClass (&wndclass))
	{
		MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
			szAppName, MB_ICONERROR) ;
		return 0 ;
	}

	hwnd = CreateWindow (szAppName,                  // window class name
		TEXT ("The Hello Program"), // window caption
		WS_OVERLAPPEDWINDOW,        // window style
		CW_USEDEFAULT,              // initial x position
		CW_USEDEFAULT,              // initial y position
		CW_USEDEFAULT,              // initial x size
		CW_USEDEFAULT,              // initial y size
		NULL,                       // parent window handle
		NULL,                       // window menu handle
		hInstance,                  // program instance handle
		NULL) ;                     // creation parameters

	ShowWindow (hwnd, iCmdShow) ;
	UpdateWindow (hwnd) ;

	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg) ;
		DispatchMessage (&msg) ;
	}
	return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	switch (message)
	{
	case WM_CREATE :
		hwndEdit = CreateWindow (TEXT ("Edit"), NULL,  
			WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |  
			WS_BORDER | ES_LEFT | ES_MULTILINE |  
			ES_AUTOHSCROLL | ES_AUTOVSCROLL,  
			20, 20, 300, 25, hwnd, (HMENU) ID_EDIT,  
			((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
		OldWndProc = (WNDPROC)SetWindowLong(hwndEdit,GWL_WNDPROC,(LONG)EditWndProc) ;
		return 0 ;
	case WM_SIZE :   
		MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;
		return 0 ;
	case WM_SETFOCUS:
		SetFocus(hwndEdit) ;
		return 0 ;
	case WM_DESTROY:
		PostQuitMessage(0) ;
		return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}

LRESULT CALLBACK EditWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static char KeyMsg ;
	switch(message)
	{
	case WM_CHAR :
		KeyMsg = LOWORD(wParam);
		if(KeyMsg == '0' || KeyMsg == '1'  || KeyMsg == VK_RETURN)
			//只能输入1或者0,这里若是不处理Enter,
			//则还给windows自己处理,后下面的WM_KEYDOWN又要处理,会导致两次弹框。
		{
			return CallWindowProc(OldWndProc,hwnd,message,KeyMsg,lParam) ;
		}
	case WM_KEYDOWN :
		if(LOWORD(wParam) == VK_RETURN)  //如果键盘输入Enter
		{
			MessageBox(NULL,"Pressed Enter in New","Edit",MB_OK|MB_ICONINFORMATION) ;
			SetFocus(hwndEdit);
		}
		return 0 ;
	case WM_DESTROY:
		PostQuitMessage(0) ;
		return 0 ;
	}
	return CallWindowProc(OldWndProc,hwnd,message,wParam,lParam) ;
}


在WNDCLASSEX 结构的成员 lpfnWndProc 指出了窗口函数地址。在WM_CREATE的时候创建一个编辑框。接着是

OldWndProc = (WNDPROC)SetWindowLong(hwndEdit,GWL_WNDPROC,(LONG)EditWndProc) ;
通过SetWindowLong来改变窗口的属性,第二个参赛GWL_WNDPROC 设置新的窗口处理函数地址

1. 用参数GWL_WNDPROC调用SetWindowLong函数,如果调用成功那么返回值就是与调用功能相联系的一个32位的整数。在这里我们返回的值保存在 OldWndPro中,下面的将不感兴趣的消息返回给windows还需要这个参数。下面是SetWindowLong函数MSDN上的解释。

----------------------------------------------------------------------------

Note: This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function

if you use SetWindowLong with the GWL_WNDPROC index to replace the window procedure, the window procedure must conform to the guidelines specified in the description of the WindowProc callback function

----------------------------------------------------------------------------

接着我们看看EditWndProc函数,这个是我们自己定义的函数,但是样子要和windows自己的窗口过程函数一样,不然可就识别不出的。看看这个函数,其实也和windows自己的窗口过程函数差不多,也是消息的处理,等价的,我们处理感兴趣的消息,对于不需要处理的消息我们就交还给windows自己的函数处理,我们要截获我们需要的即可,WM_CHAR中我们只处理‘0’,‘1’或者Enter然后调用

LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
lpPrevWndFunc = 窗口原来函数的地址,剩下的四个参数就是发给自定义函数的参数。
我们可以通过第四个参数创传回去。把不感兴趣的字符消息丢了(也就输不到编辑框上),感兴趣的字符消息传回windows自己函数,就这样神不知鬼不觉的“偷换”了函数。
看下我的代码注释,
KeyMsg == VK_RETURN
如果没有这个的话就会出现弹两次框的结果,这个可不是我们想要的,为什么会出现两次呢??我调试了下,第一次是直接在消息
WM_KEYDOWN中
if(LOWORD(wParam) == VK_RETURN)一次弹框,然后程序往下走竟然到了WM_CHAR里面,Enter是字符消息,,但是我们如果没有
KeyMsg == VK_RETURN的话直接会被传回windows自己的消息过程函数。Enter在消息列队里总得处理吧?对应的消息处理存在啊
case WM_KEYDOWN 所以又弹了一次框。

上面就是一个小程序,毕竟是新手,可能有错,希望指正,不甚感激!



参考资料《windows+sdk编程系列文章》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值