《windows核心编程》(七)-----------2013.8.3
一、概念
窗口类和窗口的附加数据
1. 开缓冲区
int cbClsExtra - 以字节为单位,一般为4的倍数
2. 存入数据
利用SetClassLong函数往缓冲区存入数据
DWORD SetClassLong(
HWND hWnd,//窗口句柄
int nIndex,//字节索引号(从哪个字节开始存)
LONG dwNewLong//存入的数据(因为long占4个字节,所以开缓冲区的字节数一般为4的倍数)
);
3. 读取数据
DWORD GetClassLong(
HWND hWnd,//窗口句柄
int nIndex//字节索引号(从哪个字节开始读)
);//返回 读取到的数据
窗口附加数据缓冲区的使用
1. 开缓冲区
int cbWndExtra - 以字节为单位,一般为4的倍数
2. 存入数据
LONG SetWindowLong(
HWND hWnd,//窗口句柄
int nIndex,//字节索引号(从哪个字节开始存)
LONG dwNewLong//存入的数据
);
3. 读取数据
LONG GetWindowLong(
HWND hWnd,//窗口句柄
int nIndex//字节索引号(从哪个字节开始读)
);//返回 读取到的数据
• 作用
注册窗口时,可以设置这两个数据内存空间 的大小。
int cbClsExtra; //窗口类的附加数据大小
int cbWndExtra; //窗口的附加数据大小
可以提供窗口类和窗口存放自己的数据的空间。
窗口类附加数据缓冲区的使用
窗口类附加数据缓冲区的使用
int cbClsExtra - 以字节为单位,一般为4的倍数
2. 存入数据
利用SetClassLong函数往缓冲区存入数据
DWORD SetClassLong(
HWND hWnd,//窗口句柄
int nIndex,//字节索引号(从哪个字节开始存)
LONG dwNewLong//存入的数据(因为long占4个字节,所以开缓冲区的字节数一般为4的倍数)
);
3. 读取数据
DWORD GetClassLong(
HWND hWnd,//窗口句柄
int nIndex//字节索引号(从哪个字节开始读)
);//返回 读取到的数据
窗口附加数据缓冲区的使用
1. 开缓冲区
int cbWndExtra - 以字节为单位,一般为4的倍数
2. 存入数据
LONG SetWindowLong(
HWND hWnd,//窗口句柄
int nIndex,//字节索引号(从哪个字节开始存)
LONG dwNewLong//存入的数据
);
3. 读取数据
LONG GetWindowLong(
HWND hWnd,//窗口句柄
int nIndex//字节索引号(从哪个字节开始读)
);//返回 读取到的数据
区别:
窗口类附加数据缓冲区: 基于该窗口类,创建的所有窗口共享的缓冲区。
窗口附加数据缓冲区: 窗口自己私有的缓冲区,即便基于同一窗口类,创建的窗口也不共享。
窗口类缓冲区与窗口缓冲区的用法示例:
// WinCreate.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include "WinCreate.h"
#include "windows.h"
HINSTANCE g_hInstance = 0;
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)
{
switch(nMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd,nMsg,wParam,lParam);
}
//注册窗口类
BOOL Register(LPTSTR lpClassName,WNDPROC wndproc)
{
WNDCLASSEX wce = {0};
wce.cbSize = sizeof(wce);
wce.cbClsExtra = 200;
wce.cbWndExtra = 200;
wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wce.hCursor = NULL;
wce.hIcon = NULL;
wce.hIconSm = NULL;
wce.hInstance = g_hInstance;
wce.lpfnWndProc = wndproc;
wce.lpszClassName = lpClassName;
wce.lpszMenuName = NULL;
wce.style = CS_HREDRAW | CS_VREDRAW;
ATOM nAtom = RegisterClassEx(&wce);
if(nAtom == 0)
{
return FALSE;
}
return TRUE;
}
//创建主窗口
HWND CreateMain(LPTSTR lpClassName,LPTSTR lpWndName)
{
HWND hWnd = CreateWindowEx(0,lpClassName,lpWndName,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,g_hInstance,NULL);
return hWnd;
}
//创建子窗口
HWND CreateChild(LPTSTR lpClassName,LPTSTR lpWndName,HWND hParent)
{
HWND hChild = CreateWindowEx(0,lpClassName,lpWndName,
WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW,100,100,200,200,
hParent,NULL,g_hInstance,NULL);
return hChild;
}
//存入数据
void SetExtra(HWND hWnd)
{
SetClassLong(hWnd,0,50);
SetWindowLong(hWnd,0,50);
}
//读取数据
void GetExtra(HWND hWnd)
{
DWORD nCalssExtra = GetClassLong(hWnd,0);
DWORD nWndExtra = GetWindowLong(hWnd,0);
char szText[256] = {0};
sprintf(szText,"窗口类:%d,窗口:%d",nCalssExtra,nWndExtra);
MessageBox(NULL,szText,"Infor",MB_OK);
}
//显示窗口
void Display(HWND hWnd)
{
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
}
//消息循环
void Message()
{
MSG nMsg = {0};
while (GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
g_hInstance = hInstance;
Register("Main",WindowProc);
HWND hWnd = CreateMain("Main","Window");
HWND hChile1 = CreateChild("Main","C1",hWnd);
HWND hChile2 = CreateChild("Main","C2",hWnd);
SetExtra(hChile1);
GetExtra(hChile2);
Display(hWnd);
MoveWindow(hChile1,300,100,200,200,TRUE);
MoveWindow(hChile2,500,100,200,200,TRUE);
Message();
return 0;
}
结果:
由于窗口类附加数据缓冲区是 基于该窗口类,创建的所有窗口共享的缓冲区,
所以调用SetClassLong函数可以读取数据50,
而窗口附加数据缓冲区:是窗口自己私有的缓冲区,即便基于同一窗口类,创建的窗口也不共享,
所以调用SetWindowLong函数时不能读取缓冲区中的数据。
因为LONG型数据占用内存为4位,而指针占用内存也为4位,所以可以讲指针传入缓冲区,这样就可以存取不同类型的数据。
将指针传入缓冲区读取数据示例:
// WinCreate.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include "WinCreate.h"
#include "windows.h"
HINSTANCE g_hInstance = 0;
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)
{
switch(nMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd,nMsg,wParam,lParam);
}
//注册窗口类
BOOL Register(LPTSTR lpClassName,WNDPROC wndproc)
{
WNDCLASSEX wce = {0};
wce.cbSize = sizeof(wce);
wce.cbClsExtra = 200;
wce.cbWndExtra = 200;
wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wce.hCursor = NULL;
wce.hIcon = NULL;
wce.hIconSm = NULL;
wce.hInstance = g_hInstance;
wce.lpfnWndProc = wndproc;
wce.lpszClassName = lpClassName;
wce.lpszMenuName = NULL;
wce.style = CS_HREDRAW | CS_VREDRAW;
ATOM nAtom = RegisterClassEx(&wce);
if(nAtom == 0)
{
return FALSE;
}
return TRUE;
}
//创建主窗口
HWND CreateMain(LPTSTR lpClassName,LPTSTR lpWndName)
{
HWND hWnd = CreateWindowEx(0,lpClassName,lpWndName,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,g_hInstance,NULL);
return hWnd;
}
//创建子窗口
HWND CreateChild(LPTSTR lpClassName,LPTSTR lpWndName,HWND hParent)
{
HWND hChild = CreateWindowEx(0,lpClassName,lpWndName,
WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW,100,100,200,200,
hParent,NULL,g_hInstance,NULL);
return hChild;
}
//存入数据
void SetExtra(HWND hWnd)
{
char *pszText = "Hello Extra";
SetClassLong(hWnd,0,(LONG)pszText);
SetWindowLong(hWnd,0,50);
}
//读取数据
void GetExtra(HWND hWnd)
{
DWORD nCalssExtra = GetClassLong(hWnd,0);
DWORD nWndExtra = GetWindowLong(hWnd,0);
char * pszText = (char *)nCalssExtra;
char szText[256] = {0};
sprintf(szText,"窗口类:%s,窗口:%d",pszText,nWndExtra);
MessageBox(NULL,szText,"Infor",MB_OK);
}
//显示窗口
void Display(HWND hWnd)
{
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
}
//消息循环
void Message()
{
MSG nMsg = {0};
while (GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
g_hInstance = hInstance;
Register("Main",WindowProc);
HWND hWnd = CreateMain("Main","Window");
HWND hChile1 = CreateChild("Main","C1",hWnd);
HWND hChile2 = CreateChild("Main","C2",hWnd);
SetExtra(hChile1);
GetExtra(hChile2);
Display(hWnd);
MoveWindow(hChile1,300,100,200,200,TRUE);
MoveWindow(hChile2,500,100,200,200,TRUE);
Message();
return 0;
}
运行结果:
显示窗口:
ShowWindow(hWnd,.....)
{
根据hWnd,可以获取某块内存中保存的关于光口的各项数据
根据获取的数据,将窗口在显示器(显卡)中绘制出来
}
更新窗口:
UpdateWindow(hWnd);
消息和消息机制
Windows消息机制
只抓取本进程的消息。
BOOL GetMessage(
LPMSG lpMsg,//存放获取到的消息buff
HWND hWnd,//窗口句柄
UINT wMsgFilterMin,//获取消息的最小ID
UINT wMsgFilterMax//获取消息的最大ID
);
翻译消息:
TranslateMessage - 只翻译按键消息
消息派发:
DispatchMessage(&nMsg)
{
根据nMsg.hWnd --> 内存 --> "Main" --> 匹配窗口类名
一旦匹配成功,调用这个窗口类中保存的函数WndProc回到自己的代码
WndProc(nMsg.hWnd,nMsg.message,nMsg.wParam,nMsg.lParam);
}
•程序执行机制
过程驱动 - 程序的执行过程是按照预定好的顺序执行。
事件驱动 - 程序的执行是无序,用户可以根据需要随机触发相应的事件。
•Win32窗口程序就是采用 事件驱动 方式执行,也就是 消息机制。
什么是消息?
•当系统通知窗口工作时,就采用消息的方式派发给窗口。
•消息组成:
窗口句柄
消息ID
消息的两个参数
消息产生的时间
消息产生时的鼠标位置
typedef struct { HWND hwnd; //窗口句柄
UINT message;//消息ID
WPARAM wParam;//消息附加参数1
LPARAM lParam; //消息附加参数2
DWORD time; //消息产生时的系统时间
POINT pt;//消息产生时鼠标的位置
} MSG, *PMSG;
typedef struct { HWND hwnd; //窗口句柄
UINT message;//消息ID
WPARAM wParam;//消息附加参数1
LPARAM lParam; //消息附加参数2
DWORD time; //消息产生时的系统时间
POINT pt;//消息产生时鼠标的位置
} MSG, *PMSG;
窗口处理函数和消息
(CALLBACK为回调,回调是指申明一个函数让操作系统自己调用,例如WinMain函数与WindowProc函数)
•每个窗口都必须具有窗口处理函数。
LRESULT CALLBACK WindowProc(
HWND hwnd, //窗口句柄
UINT uMsg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam //消息参数
);
•当系统通知窗口时,会调用窗口处理函数同时,将消息ID和消息参数传递给窗口处理函数。
•在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,例如DefWindowProc(默认处理)。
消息相关函数
获取消息:只抓取本进程的消息。
BOOL GetMessage(
LPMSG lpMsg,//存放获取到的消息buff
HWND hWnd,//窗口句柄
UINT wMsgFilterMin,//获取消息的最小ID
UINT wMsgFilterMax//获取消息的最大ID
);
lpMsg - 当获取到消息后,将消息的参数存放到MSG结构中。
hWnd - 获取到hWnd所指定窗口的消息。
wMsgFilterMin和wMsgFilterMax -只能获取到由它们指定的消息范围内的消息,
如果都为0,表示没有范围。
翻译消息:
TranslateMessage - 只翻译按键消息
BOOL TranslateMessage(
CONST MSG *lpMsg //要翻译的消息地址
);
检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行。
消息派发:
•DispatchMessage - 派发消息。将消息派发到该消息所属窗口的窗口处理函数上。
{
根据nMsg.hWnd --> 内存 --> "Main" --> 匹配窗口类名
一旦匹配成功,调用这个窗口类中保存的函数WndProc回到自己的代码
WndProc(nMsg.hWnd,nMsg.message,nMsg.wParam,nMsg.lParam);
}