源自网络收集,仅供参考
实验三收集到两份完整报告,这是其二,另一份见本专栏上一篇文章。
1 实验要求
· 整体目的:理解、窗口之间的消息传送、调用栈;掌握光标、图标、菜单的制作和使用方
式 ; 掌 握 菜 单 的 响 应 方 式 、 快 捷 菜 单 的 载 入 及 使 用 方 式 ; 理 解
GetWindowLong/SetWindowLong, GetClassLong/SetClassLong 函数的用法。
· 实验内容过程:
1. 改进 ChildWin 示例程序,在子窗口内显示出窗口特定信息(窗口 ID 号);尝试在每个
子窗口中绘制特定颜色的矩形
在子窗口的窗口过程函数的刷新处理消息中增添处理代码;
用 GetWindowLong 函数取出子窗口 ID;
根据子窗口 ID 设定子窗口内需要显示的信息(使用 wsprintf 函数组织待显示的字
符串)
尝试在子窗口中绘制特定颜色的矩形(使用 CreateSolidBrush、 Rectangle 函数)
2. 跟踪消息传递过程
在 Childwin 的主窗口过程函数的 WM_CREATE、 WM_PAINT、 WM_COMMAND、
WM_LBUTTONDOWN 处设置断点
在子窗口的 WM_CREATE、 WM_PAINT、WM_CHILDWIN、 WM_LBUTTONDOWN
处设置断点
运行程序,查看各断点的响应过程,以此理解消息传递的次序
3. 设计、实现一个软键盘
用子窗口实现一个软键盘(在窗口的客户区用子窗口绘制一个仿真的键盘),鼠标点击
每个子窗口,可输入不同的字符。将输入的字符显示到窗口中。
4. 在自己的工作目录中建立新工程用于控件测试〔可以将 Easywin 工程的 easywin.c 拷贝
到新工程的目录中,修改文件名为 ctltest.cpp(后缀名改为.cpp),编译、链接并使程序
正常工作(包括修正编译错误)〕。
5. * 用 Button 完成 4*所要求的软键盘实现。比较与原有实现的不同之处。
6. 创建各种类型的控件子窗口,包括 static、 edit、 listbox、 combobox、 button、 scrollbar
等;在主窗口的窗口过程函数中增加相应的消息处理实现主窗口与子窗口的通信(可尝
试设置控件的标题/内容,获取控件的标题/内容以及在控件有点击等操作时在主窗口给
出提示)。
7. 同步骤 5,新建用于资源测试的工程。
8. 增加新的资源:光标、图标,并正确显示
用资源编辑器编辑一个光标(彩色),注意删除资源中不需要的 image 类型,否则
显示光标可能不正确。
为光标设定热点(hot spot)
用资源编辑器编辑一个图标
修改注册窗口类的代码,使程序能正常使用自定义的光标和图标
用 DrawIcon 函数,在 WM_PAINT 消息中增加处理,在窗口客户区中显示自定义
面向对象编程技术实验报告
2
的光标及图标。
创建一个另外光标供后续实验步骤使用
9. 增加菜单
菜单中建立一个菜单项命令以修改窗口使用的光标,可命名为“ChangeCursor”,
对应的 ID 假设为 ID_CHANGECURSOR
修改注册窗口类的代码,使程序运行时能正确载入菜单
换一种方式,通过修改创建窗口(CreateWindow) 的代码,使菜单能正确载入
10. 增加菜单消息响应代码
增加 WM_COMMAND 的消息处理
参照第五章菜单示例代码中的处理方式,响应菜单命令
对 ID_CHANGECURSOR 命令的响应为:改变窗口使用的光标,可以使用
SetClassLong (hwnd, GCL_HCURSOR,
(LONG)LoadCursor((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
MAKEINTRESOURCE(IDC_CURSOR2) ) );
11. 处理鼠标右键,增加快捷菜单处理
右键发出的菜单命令同样是通过 WM_COMMAND 消息响应的,增加快捷菜单之
后,程序应该能通过主菜单或快捷菜单两种方式发出同样的命令
12. 将本次实验做好的工程的编译、链接增加到程序构建使用的批处理文件中,用批处理文
件一次性自动构建实验一、二、三的所有工程。
2 实验过程及结果
2.1 改进 ChildWin 示例程序, 在子窗口内显示出窗口特定信息(窗
口 ID 号);尝试在每个子窗口中绘制特定颜色的矩形
如图所示,显示 id 号并绘制绿色矩形
2.2跟踪消息传递过程
1.在ChildWin的主窗口过程函数的WM_CREATE、WM_PAINT、WM_COMMAND、WM_LBUTTONDOWN处设置断点
·WM_CREATE:
创建窗口时产生中断
·WM_COMMAND:
命中子窗口时程序产生中断
·WM_LBUTTONDOWN:
当鼠标左键按下时程序会产生中断
2.在子窗口的WM_CREATE、WM_PAINT、WM_CHILDWIN、WM_LBUTTONDOWN处设置断点
·WM_CREATE:
·WM_PAINT:
主窗口绘制完并正常显示,在子窗口绘制前产生中断。在逐过程的操作下,从第一个子窗口开始,随着WM_PAINT的循环逐个绘制显示。
·WM_CHILDWIN:
在鼠标命中子窗口时,主窗口收到通知消息,并发送通知消息至WM_CHILDWIN,遂产生中断
·WM_LBUTTONDOWN:
3.运行程序,查看各断点的响应过程,以此理解消息传递的次序:
通过设置断点并进行相应调试后可以得知:当鼠标左键命中子窗口时,子窗口的WM_LBUTTONDOWN产生响应,发送消息至主窗口WM_COMMAND,WM_COMMAND确定子窗口被命中后发送消息至子窗口的WM_CHILDWIN,WM_CHILDWIN在收到消息后将子窗口命中标志置0。
2.3 设计、实现一个软键盘,鼠标点击每个子窗口可输入不同的字符,将输入的字符显示到窗口中
2.4 建立新工程用于控件测试,将Easywin工程的easywin.c拷贝到新工程的目录中,修改文件名为ctltest.cpp(后缀名改为.cpp),编译、链接并使程序正常工作(包括修正编译错误)
2.5 *用Button完成4*所要求的软键盘实现
2.6 创建各种类型的控件子窗口,在主窗口的窗口过程函数中增加相应的消息处理实现主窗口与子窗口的通信
创建EDIT、LISTBOX、BUTTON窗口进行测试:
1.EDIT窗口可以实现文本输入:
2.LISTBOX窗口在点击一行时会弹窗显示当前选项值:
3.BUTTON窗口在鼠标左键单击后会弹窗提示button已被按下:
2.7 新建用于资源测试的工程
2.8 增加新的资源:光标、图标,并正确显示
1.用资源编辑器编辑一个光标(彩色)
2.为光标设定热点(hot spot)
3.用资源编辑器编辑一个图标
4.修改注册窗口类的代码,使程序能正常使用自定义的光标和图标
5.用DrawIcon函数,在WM_PAINT消息中增加处理,在窗口客户区中显示自定义的光标及图标。
6.创建一个另外光标供后续实验步骤使用
2.9 增加菜单
1.菜单中建立一个菜单项命令以修改窗口使用的光标,可命名为“ChangeCursor”,对应的ID假设为ID_CHANGECURSOR
2.修改注册窗口类的代码,使程序运行时能正确载入菜单
3.换一种方式,通过修改创建窗口(CreateWindow)的代码,使菜单能正确载入
2.10 增加菜单消息响应代码
增加WM_COMMAND的消息处理,对ID_CHANGECURSOR命令的响应为:改变窗口使用的光标
菜单命令ChangeCursor后:
2.11 处理鼠标右键,增加快捷菜单处理
右键发出的菜单命令同样是通过WM_COMMAND消息响应的,增加快捷菜单之后,程序应该能通过主菜单或快捷菜单两种方式发出同样的命令
2.12 将本次实验做好的工程的编译、链接增加到程序构建使用的批处理文件中,用批处理文件一次性自动构建实验一、二、三的所有工程。
3 实验总结
本次实验相比之前的两次实验难度提升了很多,增添了需要自己思考摸索实现的部分,也正因此本次实验使我收获了很多:
1. 对GetWindowLong等函数有了更深的理解,从提取子窗口的ID,到绘制矩形,通过对相关函数的应用及断点调试,我对父窗口、子窗口之间的消息通知联系及相关操作有了更透彻的理解,只有理清父窗口与子窗口之间的关系,才能简洁有效地实现自己想要的东西。
2. 通过设计实现软键盘,我了解了更多子窗口的选中、释放,及父窗口做出响应的操作。并且在创作软键盘的过程中也对子窗口的布局有了更多的思考,在自己的探索尝试下,尽力做出了更加美观的软键盘,在努力实验的过程中也收获了一种成就感。回过头看,软键盘的制作并不复杂,但对于之前的我来说还是一个毫无头绪的项目,一个软键盘的从无到有也见证了我在课程中知识的学习、积累过程。
3. 控件测试部分更多的是课上程序示例的延伸,难度并不大,主要了解学习的知识是各个空间的结构和在窗口中如何去编写、如何应用来达到自己想要的效果。
4. 资源测试同上,在老师的耐心讲解下,课上就已经可以实现大部分实验要求的功能,自己的课下实验中主要是进行了程序对资源的应用等操作。
5. 相比上两个部分,我在菜单部分遇到了一些困难。跟着老师课上的操作,菜单的建立和引用等都没有实现的问题,在菜单功能的实现部分,根据实验中SetClassLong函数几次尝试后也可以实现。相对费时的部分在于快捷菜单的制作使用,这部分是需要自学制作的。在经过查阅资料及不断尝试之后,最终也完成了快捷菜单更换光标的操作。
6. 总的来说,本次实验是比较成功的,虽然耗时较多,但绝大部分项目都是在自己的努力下完成的,对Windows程序设计的知识有了更深的理解,一个个项目的实现也让我感到有付出就有回报。但与此同时,由于我实验进度较慢,选做项目的BUTTON软键盘没有实现,在下次实验中我会更加认真努力,及时完成实验,做好每一项实验任务。
附录:关键代码
用GetWindowLong函数取出子窗口ID;
根据子窗口ID设定子窗口内需要显示的信息(使用wsprintf函数组织待显示的字符串)
int number = GetWindowLong(hWnd, GWL_ID);//取出子窗口ID
char string[20];
wsprintf(string, TEXT("%d"), number);
case WM_PAINT:
DrawText(hdc, string, //显示子窗口ID号
-1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
绘制特定颜色矩形
HBRUSH hColorPen;
HGDIOBJ hGdiobj;
hColorPen = CreateSolidBrush(RGB(10, 100, 100));// 创建画笔并选择颜色
hGdiobj = SelectObject(hdc, hColorPen);//选择区域使用画笔绘制
Rectangle(hdc, 5, 20, 45, 45);
DeleteObject(hColorPen);//释放画笔
设计、实现一个软键盘:
#define ROW_NUM 4 //行数
#define COL_NUM 16 //列数
INT t = 0;//全局变量t,当第一次选中子窗口时即变为1,用于避免软键盘初始状态下输入字符
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hChildWnd[COL_NUM][ROW_NUM]; //子窗口句柄
static int nXBox, nYBox; //子窗口宽度和高度
static WORD nChildWin_ID; //被命中的子窗口标识
static int nRow, nCol; //被命中的子窗口位置(即行列号)
int x, y;
WNDCLASS wcChild; //子窗口类
switch (message)
{
case WM_CREATE:
...
//创建类似“画图”程序中绘图工具栏的各工具子窗口
for (y = 0; y < ROW_NUM; y++)
for (x = 0; x < COL_NUM; x++)
{
nChildWin_ID = y << 4 | x; //子窗口标识值 相当于y * 2^4 +4
hChildWnd[x][y] = CreateWindow(szChildName, NULL,
WS_CHILDWINDOW | WS_DLGFRAME | WS_VISIBLE,
0, 0, 0, 0,
hWnd,
(HMENU)(nChildWin_ID),//子窗口id号
//0,
hInst, NULL);
}
return 0;
case WM_SIZE:
nXBox = (LOWORD(lParam)*4/5)/ COL_NUM;
nYBox = (HIWORD(lParam)*2/5) / ROW_NUM;
//主窗口大小改变时,重新确定各子窗口在主窗口客户区的位置
for (y = 0; y < ROW_NUM; y++)
for (x = 0; x < COL_NUM; x++)
{
MoveWindow(hChildWnd[x][y], (x * nXBox + LOWORD(lParam)/10),
(y * nYBox + HIWORD(lParam) / 3), nXBox, nYBox, TRUE);//x y 宽 高
}
return 0;
case WM_COMMAND: //子窗口被命中的通知消息
//当子窗口被命中时发送此消息通知主窗口,以便由主窗口确定是否命中另一个子窗口,
//若是,则发送消息通知子窗口函数改变前一次被命中子窗口的状态,
if (!(LOWORD(wParam) == nChildWin_ID))
{
SendMessage((HWND)hChildWnd[nCol][nRow], WM_CHILDWIN, 0, 0L);
//记录新的被命中的子窗口
nChildWin_ID = LOWORD(wParam);
nCol = (nChildWin_ID > 15) ? (nChildWin_ID - 16) : nChildWin_ID;
nRow = nChildWin_ID >> 4;
}
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_PAINT:
if (t)//有子窗口被命中后开始绘制显示对应的字符
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
LONG l = 0;
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &ps);
int number = nChildWin_ID;
static char string[100] = { 0 };
static int n = 0;
string[n] = number + 65;
n++;
DrawText(hdc, string, n, &rect, DT_SINGLELINE);
EndPaint(hWnd, &ps);
}
return 0;
....
return DefWindowProc(hWnd, message, wParam, lParam);
} //函数 WinProc 结束
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
case WM_LBUTTONDOWN:
//新的子窗口被命中
if (GetWindowLong(hWnd, 0) == 0)
{
t = 1;
//置该子窗口命中标志为1
SetWindowLong(hWnd, 0, 1);
//发送消息通知主窗口
SendMessage(GetParent(hWnd), WM_COMMAND,
//(WPARAM) MAKELONG ((WORD)GetWindowWord (hWnd, GWL_ID), (WORD)0),
GetWindowLong(hWnd, GWL_ID),
(LPARAM)hWnd);
//重绘子窗口客户区
GetClientRect(hWnd, &rect);
InvalidateRect(hWnd, &rect, TRUE);
}
return 0;
case WM_CHILDWIN: //主窗口发送的通知消息,改变原命中子窗口的状态
//置子窗口命中标志为0
SetWindowLong(hWnd, 0, 0);
//恢复子窗口初始显示方式
GetClientRect(hWnd, &rect);
InvalidateRect(hWnd, &rect, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
Rectangle(hdc, 10, 10, 40, 40);//左高右底
for (int j = 0; j < COL_NUM; j++)
for (int i = 0; i < ROW_NUM; i++) //列数
{
char szBuff[50];
char key = GetWindowLong(hWnd, GWL_ID) + 65;
wsprintf(szBuff, TEXT("%c"), key); //绘制子窗口的字母
DrawText(hdc, szBuff,
-1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
} //函数 ChildWinProc 结束
创建各种类型的控件子窗口,在主窗口的窗口过程函数中增加相应的消息处理实现主窗口与子窗口的通信
#define ITEMCOUNT 5
WCHAR gListItems[ITEMCOUNT][60] = {
L"MSG msg;",
L"if (!InitWindow (hInstance, iCmdShow))",
L"return FALSE;",
L"GetMessage (&msg, NULL, 0, 0)",
L"OK"
};
LRESULT CALLBACK WinProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
....
switch (message)
{
case WM_CREATE: //创建消息
hWndEdit = CreateWindow(TEXT("EDIT"), //EDIT
NULL, //无标题
WS_CHILD | WS_VISIBLE | WS_HSCROLL | //编辑控件风格
WS_VSCROLL | WS_BORDER | ES_LEFT |
ES_MULTILINE | ES_AUTOHSCROLL |
ES_AUTOVSCROLL,
0, 0, 0, 0,
hWnd, //父窗口句柄
(HMENU)1, //编辑控件子窗口标识
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
hWndList = CreateWindow(TEXT("LISTBOX"), //LISTBOX NULL, //无标题
WS_CHILD | WS_VISIBLE | LBS_STANDARD, //编辑控件风格
0, 0, 0, 0,
hWnd, //父窗口句柄
(HMENU)2, //编辑控件子窗口标识
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
hWndButton = CreateWindow(TEXT("BUTTON"), //BUTTON
NULL, //无标题
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
0, 0, 0, 0,
hWnd, //父窗口句柄
(HMENU)3, //编辑控件子窗口标识
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
for (int i = 0; i < ITEMCOUNT; i++)
{
SendMessage(hWndList, LB_ADDSTRING, 0, (LPARAM)gListItems[i]);
}
return 0;
case WM_SIZE:
MoveWindow(hWndEdit, 0, 0, LOWORD(lParam) / 3, HIWORD(lParam), TRUE);
MoveWindow(hWndList, LOWORD(lParam) / 3 + 2, 0, LOWORD(lParam) / 3 - 5, HIWORD(lParam), TRUE);
MoveWindow(hWndButton, 2*LOWORD(lParam)/3+2 , 0, 2*LOWORD(lParam)/3-5 , HIWORD(lParam), TRUE);
return 0;
case WM_COMMAND:
WORD w, W1, W2, W3;
w = LOWORD(wParam);
W1 = HIWORD(wParam);
W2 = LOWORD(lParam);
W3 = HIWORD(lParam);
//编辑控件通知消息
if ((LOWORD(wParam)) == 1)
{
}
else if ((LOWORD(wParam)) == 2)
{
if (HIWORD(wParam) == LBN_SELCHANGE)
{
WCHAR msg[32];
UINT u = SendMessage((HWND)lParam, LB_GETCURSEL, 0, 0);
wsprintf(msg, L"%d", u);
MessageBox(hWnd, msg, L"SEL", MB_OK);
}
}
else if ((LOWORD(wParam)) == 3)
{
MessageBox(hWnd, TEXT("Button按下了!"), TEXT("窗口消息"), MB_OK);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
//调用缺省消息处理过程
return DefWindowProc (hWnd, message, wParam, lParam);
} //函数 WinProc 结束
修改注册窗口类代码,使程序能正常使用自定义的光标和图标
wcMainWnd.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//图标
wcMainWnd.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));//光标
用DrawIcon函数,在WM_PAINT消息中增加处理,在窗口客户区中显示自定义的光标及图标
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
HCURSOR hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
...
DrawIcon(hdc, 160, 80, hIcon);
DrawIcon(hdc, 100, 80, hCursor);
处理鼠标右键,增加快捷菜单处理
HMENU hSubMenu;//全局变量
static BOOL InitWindow(HINSTANCE hInstance, int iCmdShow)
{
...
HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU2));
hSubMenu = GetSubMenu(hMenu, 1);
.....
}
LRESULT CALLBACK WinProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
case WM_RBUTTONDOWN: //鼠标消息
GetCursorPos(&stPos);//获取光标位置
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, stPos.x, stPos.y, NULL, hWnd, NULL);//在指定位置显示快捷方式菜单
return 0;
...
}