本文档为《Windows环境下32位汇编语言程序设计》第五章使用资源的部分代码的C语言版,并提供一些必要的补充说明。
5.1 菜单资源
122页:Menu.rc
资源代码(*.rc)文件分既不是汇编语言也不完全是C语言。资源代码可以直接放在C语言工程文件中编译。但考虑到读者复制方便,本文档会重新抄写一遍原书中的资源代码文件。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
ICO_MAIN ICON "Main.ico" //一定要保证main.ico文件存在
IDM_MAIN menu discardable
BEGIN
popup "文件(&F)"
BEGIN
menuitem "打开文件(&O)...",IDM_SETFONT
menuitem "关闭文件(&C)...",IDM_OPTION
menuitem separator
menuitem "退出(&X)",IDM_EXIT
END
popup "查看(&V)"
BEGIN
menuitem "字体(&F)...\tAlt+F",IDM_SETFONT
menuitem "背景色(&B)...\tCtrl+Alt+B",IDM_SETCOLOR
menuitem separator
menuitem "被禁用的菜单项",IDM_INACT,INACTIVE
menuitem "被灰化的菜单项",IDM_GRAY,GRAYED
menuitem separator
popup "工具栏(&T)"
BEGIN
menuitem "标准按钮(&S)",IDM_TOOLBAR
menuitem "文字标签(&C)",IDM_TOOLBARTEXT
menuitem "命令栏(&I)",IDM_INPUTBAR
END
menuitem "状态栏(&U)",IDM_STATUSBAR
END
popup "帮助(&H)",HELP
BEGIN
menuitem "帮助主题(&H)\tF1",IDM_HELP
menuitem separator
menuitem "关于本程序(&A)...",IDM_ABOUT
END
END
IDA_MAIN accelerators
// 注意书上这块少个空格……
BEGIN
"B", IDM_SETCOLOR,VIRTKEY,CONTROL,ALT
"F", IDM_SETFONT,VIRTKEY,ALT
END1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176// include 文件定义
// 稍微解释一下,C语言WIN32项目只需要包含windows.h即可,其他动态库会由编译器自动链接,不需要像汇编语言那样手动的链接动态库。
// EQU等值定义,对应的就是C语言的define,直接抄Menu.rc就可以了。或者你也可以单独写一个头文件,Menu.c和Menu.rc共同include这个头文件
// 全局变量
HINSTANCE hInstance;
HWND hWinMain;
HMENU hMenu;
HMENU hSubMenu;
// 字符串常量。因为汇编指令里不能直接使用字符串,所以必须提前定义字符串,指令里传指针。
// 但是C语言可以啊,所以本部分常量仅作一次声明,接下来的代码部分我会直接把常量展开。
LPCSTR szClassName = "Menu Example";
LPCSTR szCaptionMain = "Menu";
LPCSTR szMenuHelp = "帮助主题(&H)";
LPCSTR szMenuAbout = "关于本程序(&A)...";
LPCSTR szCaption = "菜单选择";
LPCSTR szFormat = "您选择了菜单命令:%08x";
//代码声明
//代码部分
void WINAPI _DisplayMenuItem(unsigned _dwCommandID)
{
char szBuffer[256];
wsprintfA(szBuffer, "您选择了菜单命令:%08x", _dwCommandID);
MessageBoxA(hWinMain, szBuffer, "菜单选择", MB_OK);
}
void WINAPI _Quit(void)
{
DestroyWindow(hWinMain);
PostQuitMessage(NULL);
}
// 在FirstWindow.c上略有改动
// 函数定义
LRESULT CALLBACK _ProcWinMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* 窗口过程 回调函数*/
switch (uMsg)
{
case WM_CREATE:
{
HMENU hSysMenu;
hSubMenu = GetSubMenu(hMenu, 1);
// 在系统菜单中添加菜单项
hSysMenu = GetSystemMenu(hWnd, 0);
AppendMenuA(hSysMenu, MF_SEPARATOR, 0, NULL);
AppendMenuA(hSysMenu, 0, IDM_HELP, "帮助主题(&H)");
AppendMenuA(hSysMenu, 0, IDM_ABOUT, "关于本程序(&A)...");
}
break;
case WM_COMMAND:
{
// 处理菜单及加速键消息
_DisplayMenuItem(wParam);
wParam = wParam & 0xFFFF;
if (wParam == IDM_EXIT)
{
_Quit();
}
else if (wParam >= IDM_TOOLBAR && wParam <= IDM_STATUSBAR)
{
if (GetMenuState(hMenu, wParam, MF_BYCOMMAND) == MF_CHECKED)
{
CheckMenuItem(hMenu, wParam, MF_UNCHECKED);
}
else
{
CheckMenuItem(hMenu, wParam, MF_CHECKED);
}
}
else if (wParam >= IDM_BIG && wParam <= IDM_DETAIL)
{
CheckMenuRadioItem(hMenu, IDM_BIG, IDM_DETAIL, wParam, MF_BYCOMMAND);
}
}
break;
case WM_SYSCOMMAND:
{
unsigned mess = wParam & 0xFFFF;
if (mess == IDM_HELP || mess == IDM_ABOUT)
{
_DisplayMenuItem(wParam, 0);
}
else
{
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}
break;
}
case WM_RBUTTONDOWN:
{
POINT stPos;
GetCursorPos(&stPos);
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, stPos.x, stPos.y, NULL, hWnd, NULL);
}
break;
case WM_CLOSE:
_Quit();
break;
default:
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI _WinMain()
{
WNDCLASSEXA stWndClass;
MSG stMsg;
HACCEL hAccelerator;
hInstance = GetModuleHandleA(NULL);
// 注册菜单和快捷键
hMenu = LoadMenuA(hInstance, (LPCSTR)IDM_MAIN);
hAccelerator = LoadAcceleratorsA(hInstance, (LPCSTR)IDA_MAIN);
// 注册窗口类,这里获取了hIcon
RtlZeroMemory(&stWndClass, sizeof(stWndClass));
stWndClass.hIcon = LoadIconA(hInstance, (LPCSTR)ICO_MAIN);
stWndClass.hIconSm = stWndClass.hIcon;
stWndClass.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
stWndClass.hInstance = hInstance;
stWndClass.cbSize = sizeof(WNDCLASSEX);
stWndClass.style = CS_HREDRAW | CS_VREDRAW;
stWndClass.lpfnWndProc = _ProcWinMain;
stWndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
stWndClass.lpszClassName = szClassName;
RegisterClassExA(&stWndClass);
// 建立并显示窗口,这里传了个hMenu
hWinMain = CreateWindowExA(WS_EX_CLIENTEDGE, szClassName, szCaptionMain, WS_OVERLAPPEDWINDOW, 100, 100, 400, 300, NULL, hMenu, hInstance, NULL);
ShowWindow(hWinMain, SW_SHOWNORMAL);
UpdateWindow(hWinMain);
// 消息循环
while (1)
{
if (GetMessageA(&stMsg, NULL, 0, 0) == 0)
break;
if (TranslateAcceleratorA(hWinMain, hAccelerator, &stMsg) == 0)
{
TranslateMessage(&stMsg);
DispatchMessageA(&stMsg);
}
}
return 0;
}
// 主函数,子系统选择windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
return _WinMain();
}
首先是新的几个需要区分A和W版本的函数:
1 | AppendMenuA(); |
之后是资源编号的问题。
C语言中读取资源的函数,以LoadMenuA为例,它的第二个参数只支持传入字符串地址即LPCSTR。但是根据书上所说,这个参数小于0x10000时API会认为是数字,大于0x10000时会认为是字符串地址。因此我们调用这列函数的时候,只需要在编号之前做强制类型转换: 1
LoadMenuA(hInstance, (LPCSTR)IDM_MAIN);
此外,如何在C语言项目中添加并编辑资源代码(*.rc),以Visual Studio为例:
1、右键资源文件,添加新建项。 2、创建以rc结尾的文件。 3、右键选择打开方式。 4、选择C++源码编辑器 5、打开编辑rc文件,编辑完之后保存并关闭。 完成之后,你再在visual studio上双击打开这个资源文件,你会发现vs自动解析了这个资源代码。 在资源视图里可以直接右键添加资源。 事实上,读者完全可以只写一个基本的rc框架,然后用资源视图进行添加资源。
补充说明一:Windows程序的换行
无论你是用MessageBoxA还是其他显示字符串的winapi,请用""代表换行。单纯的""无法起到换行的作用。这也能解释为什么一些OnlineJudge上的测试样例需要连着两次getchar()才能过滤掉回车,就是测试样例用的是""做回车。Linux等其他操作系统一般用""代表换行。
补充说明二:数据类型 BYTE WORD DWORD
这三种数据类型可以代表Winapi中几乎所有的变量类型(除了结构体类型)。BYTE为字节,大小为1字节,对应C语言的char变量类型的大小。WORD为字,大小为2字节,对应C语言的unsigned short变量类型的大小。DWORD为双字,大小为4字节,对应C语言的unsigned int/unsigned long变量类型的大小。
Winapi中的一些变量类型比如一大堆句柄(HMENU、HWND、HINSTANCE等)归根结底都是这三种基本类型的重命名。必要时可以用强制类型转换。
补充说明三:两个重要的参数wParam和lParam
在winapi中,wParam和lParam是传递消息的重要变量,类型均为DWORD。在不同的消息中,wParam和lParam数值的含义各不相同。普遍情况下,wParam的低2字节和高2字节分别具有不同的含义。 1
2wParam & 0xFFFF; // 取低2字节的数据
wParam >> 16; // 取高2字节的数据
对于菜单和加速键来说:
lParam始终为0,wParam高2字节菜单为0、加速键为1,wParam低2字节为资源代码文件中对菜单定义的ID值。
对于按钮来说:
lParam为按钮句柄,wParam低2字节为资源代码中对按钮定义的ID值。若是通过CreateWindowEx创建的按钮,则ID为创建按钮传给CreateWindowEx时HMENU hMenu参数的值。
wParam高2字节通知码为以下宏定义: 1
2
3
4
5
6
7
8
9/*
* User Button Notification Codes
*/
wParam高2字节通知码为以下宏定义: 1
2
3
4
5
6
7
8
9
10
11/*
* Edit Control Notification Codes
*/
这里提供几个相关资料: 1
2
3
4
5
6
7
8
9MSDN文档——Windows控件:
https://learn.microsoft.com/zh-cn/windows/win32/controls/window-controls
在这里你能查找到所有winapi的控件相关的函数和宏定义。主要看控件的消息和通知码。
创建按钮实例:
https://blog.csdn.net/weixin_44499065/article/details/134073886
创建编辑框实例:
https://blog.csdn.net/ayqy42602/article/details/97931971
Windows程序的菜单分两类:系统菜单和非系统菜单。对应的加速键(快捷键)也分系统与非系统。
简单来说,非系统菜单就是程序员在资源文件中定义的菜单,点击非系统菜单的menuitem,发送的消息是WM_COMMAND;系统菜单是Windows维护的菜单,包含最大化最小化关闭等基本任务,发送的消息是WM_SYSCOMMAND。非系统菜单的句柄需要使用LoadMenu获取,而系统菜单的句柄需要使用GetSysMenu函数获取。
对于Menu.c缩写的程序,非系统菜单是这些。标题下面的菜单项及其子项,右键弹出的菜单项及其子项都是非系统菜单: 单击图标或者按Alt+Space弹出的菜单是系统菜单: 我们稍微的对Menu.c的代码改动一下,方便读者更好地区分哪部分是系统菜单哪部分是非系统菜单: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//代码部分,增加一个int i参数
void WINAPI _DisplayMenuItem(unsigned _dwCommandID, int i)
{
char szBuffer[256];
wsprintfA(szBuffer, "您选择了菜单命令:%08x\r\n%s", _dwCommandID, (i ? "非系统菜单" : "系统菜单"));
MessageBoxA(hWinMain, szBuffer, "菜单选择", MB_OK);
}
//......
case WM_COMMAND:
_DisplayMenuItem(wParam,1);
case WM_SYSCOMMAND:
_DisplayMenuItem(wParam,0);1
2
3
4
5
6
7
8
9
10
11
12
13case WM_SYSCOMMAND:
{
unsigned mess = wParam & 0xFFFF;
if (mess == IDM_HELP || mess == IDM_ABOUT)
{
_DisplayMenuItem(wParam, 0);
}
else // 不是自己新添加的消息一定要交给默认处理函数!!
{
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}
break;
}
P132页 1、加载菜单:
用CreateWindowEx指定菜单 1
2// 建立并显示窗口,这里传了个hMenu
hWinMain = CreateWindowExA(WS_EX_CLIENTEDGE, szClassName, szCaptionMain, WS_OVERLAPPEDWINDOW, 100, 100, 400, 300, NULL, hMenu, hInstance, NULL);1
hMenu = LoadMenuA(hInstance, (LPCSTR)IDM_MAIN);
加速键(快捷键)与菜单一样,需要获取句柄。 1
hAccelerator = LoadAcceleratorsA(hInstance, (LPCSTR)IDA_MAIN);
1
2
3
4
5
6
7
8
9
10
11// 在win32汇编中,eax是32位通用寄存器,其基本功能之一就是存储函数的返回值;invoke指令是调用函数,因此书上的代码意思就是TranslateAccelerator函数返回值位0时执行TranslateMessage和DispatchMessage
while (1)
{
if (GetMessageA(&stMsg, NULL, 0, 0) == 0)
break;
if (TranslateAcceleratorA(hWinMain, hAccelerator, &stMsg) == 0)
{
TranslateMessage(&stMsg);
DispatchMessageA(&stMsg);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16switch(uMsg)
{
case WM_COMMAND:
{
unsigned wID = wParam & 0xFFFF;
if(wID == 命令ID1)
{
}
else if(...)
{
}
}
break;
}
135页 菜单项的修改
程序运行中可以动态修改菜单项,由以下几个API完成: 1
2
3
4
5BOOL WINAPI AppendMenuA(HMENU hMenu, UINT uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem); //添加菜单项
BOOL WINAPI InsertMenuA(HMENU hMenu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem); //插入菜单项
BOOL WINAPI ModifyMenuA(HMENU hMenu,UINT uPosition,uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem); //修改菜单项
BOOL WINAPI RemoveMenu(HMENU hMenu,UINT uPosition,UINT uFlags); //删除菜单项
BOOL WINAPI DeleteMenu(HMENU hMenu,UINT uPosition,UINT uFlags); //删除菜单项
136页 使用系统菜单
(本人并不推荐这种做法,一是容易把程序写崩,二是很少有人知道怎样打开程序的系统菜单。) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// 窗口过程 消息循环中
case WM_CREATE:
{
HMENU hSysMenu;
hSubMenu = GetSubMenu(hMenu, 1);
// 在系统菜单中添加菜单项
hSysMenu = GetSystemMenu(hWnd, 0);
AppendMenuA(hSysMenu, MF_SEPARATOR, 0, NULL);
AppendMenuA(hSysMenu, 0, IDM_HELP, "帮助主题(&H)");
AppendMenuA(hSysMenu, 0, IDM_ABOUT, "关于本程序(&A)...");
}
// 另外说明一下,WM_CREATE是窗口或组件创建时发出的消息,类似于类的构造函数的作用。
// 窗口过程 消息循环中
case WM_SYSCOMMAND:
{
unsigned wID = wParam & 0xFFFF;
if (wID == ??)
{
// TODO...
}
else if(wID == ??)
{
// TODO...
}
else // 一定要注意这个
{
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}
break;
}
137页 右键弹出菜单
实现函数: 1
BOOL WINAPI TrackPopupMenu(HMENU hMenu,UINT uFlags,int x,int y,int nReserved,HWND hWnd,const RECT *prcRect);
1
2POINT stPos;
GetCursorPos(&stPos);1
2
3POINT stPos;
GetCursorPos(&stPos);
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, stPos.x, stPos.y, NULL, hWnd, NULL);1
HMENU WINAPI GetSubMenu(HMENU hMenu,int nPos);
1
UINT WINAPI GetMenuState(HMENU hMenu,UINT uId,UINT uFlags);
1
2
3
4
5
6
7
8
9
10
11
12
13unsigned st = GetMenuState(...);
if(st & MF_CHECKED)
{
// 表示IDM_XXX菜单项现在是选中状态
}
else if(st & MF_DISABLED)
{
}
else if(st & MF_GRAYED)
{
}1
2
3BOOL WINAPI EnableMenuItem(HMENU hMenu,UINT uIDEnableItem,UINT uEnable);
DWORD WINAPI CheckMenuItem(HMENU hMenu,UINT uIDCheckItem,UINT uCheck);
BOOL WINAPI CheckMenuRadioItem(HMENU hmenu,UINT first,UINT last,UINT check,UINT flags);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15else if (wParam >= IDM_TOOLBAR && wParam <= IDM_STATUSBAR)
{
if (GetMenuState(hMenu, wParam, MF_BYCOMMAND) == MF_CHECKED)
{
CheckMenuItem(hMenu, wParam, MF_UNCHECKED);
}
else
{
CheckMenuItem(hMenu, wParam, MF_CHECKED);
}
}
else if (wParam >= IDM_BIG && wParam <= IDM_DETAIL)
{
CheckMenuRadioItem(hMenu, IDM_BIG, IDM_DETAIL, wParam, MF_BYCOMMAND);
}
5.2 图标和光标
图标的话就是在资源文件里定义图标资源,在CreateWindowEx之前调用LoadIcon。
定义图标资源: 1
2
3
4
5
6
7// resource.rc
ICO_SMALL ICON "Small.ico"
ICO_BIG ICON "Big.ico"1
hIcon = LoadIconA(hInstance,lpIconName);
5.4 对话框
(本人推荐主窗口用CreateWindow,做子任务的子窗口用对话框) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//Dialog.rc
ICO_MAIN ICON "Main.ico"
DLG_MAIN DIALOG 50,50,113,64
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "对话框模板"
FONT 9, "宋体"
{
ICON ICO_MAIN, -1, 10, 11, 18, 21
CTEXT "简单的对话框例子\r\n用Win32ASM编写", -1, 36, 14, 70, 19
DEFPUSHBUTTON "退出(&X)", IDOK, 58, 46, 50, 14
CONTROL "", -1, "Static", SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, 6, 39, 103, 1
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
HINSTANCE hInstance;
LRESULT CALLBACK _ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HICON hIcon;
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, NULL);
break;
case WM_INITDIALOG:
hIcon = LoadIconA(hInstance, (LPCSTR)ICO_MAIN);
SendMessageA(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
break;
case WM_COMMAND:
wParam &= 0xFFFF;
if (wParam == IDOK)
{
EndDialog(hWnd, NULL);
}
break;
default:
return FALSE;
}
return TRUE;
}
// 主函数,子系统选择windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInstance = GetModuleHandleA(NULL);
DialogBoxParamA(hInstance, (LPCSTR)DLG_MAIN, NULL, (DLGPROC)_ProcDlgMain, NULL);
return 0;
}1
INT_PTR WINAPI DialogBoxParamA(HINSTANCE hInstance,LPCSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
1
(DLGPROC)函数名称
1
BOOL WINAPI EndDialog(HWND hDlg,INT_PTR nResult);
1
HWND WINAPI CreateDialogParamA(HINSTANCE hInstance,LPCSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
对话框过程,与普通窗口的过程一致:
1 | LRESULT CALLBACK _ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
注意对话框过程和普通窗口过程在使用时有以下区别(见书上描述)
在对话框中使用子窗口控件
Control.rc
1 |
|
注意在编译时,你的项目工程里要有Main.ico、Picture1.bmp和Picture2.bmp文件。 ### Control.c 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
HINSTANCE hInstance;
HBITMAP hBmp1, hBmp2;
DWORD dwPos;
LPCSTR szText1 = "Hello, World!";
LPCSTR szText2 = "嘿,你看到标题栏变了吗?";
LPCSTR szText3 = "自定义";
LRESULT CALLBACK _ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
char szBuffer[128];
HICON hIcon;
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, NULL);
DeleteObject(hBmp1);
DeleteObject(hBmp2);
break;
case WM_INITDIALOG:
// 设置标题栏图标
hIcon = LoadIconA(hInstance, (LPCSTR)ICO_MAIN);
SendMessageA(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
// 初始化组合框
SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_ADDSTRING, 0, (LPARAM)"Hello, World!");
SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_ADDSTRING, 0, (LPARAM)"嘿,你看到标题栏变了吗?");
SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_ADDSTRING, 0, (LPARAM)"自定义");
SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_SETCURSEL, 0, 0);
EnableWindow(GetDlgItem(hWnd, IDC_CUSTOMTEXT), FALSE);
hBmp1 = LoadBitmapA(hInstance, (LPCSTR)IDB_1);
hBmp2 = LoadBitmapA(hInstance, (LPCSTR)IDB_2);
// 初始化单选钮和复选框
CheckDlgButton(hWnd, IDC_SHOWBMP, BST_CHECKED);
CheckDlgButton(hWnd, IDC_ALOW, BST_CHECKED);
CheckDlgButton(hWnd, IDC_THICKFRAME, BST_CHECKED);
// 初始化滚动条
SendDlgItemMessageA(hWnd, IDC_SCROLL, SBM_SETRANGE, 0, 100);
break;
case WM_COMMAND:
switch (wParam & 0xFFFF)
{
case IDCANCEL:
EndDialog(hWnd, NULL);
DeleteObject(hBmp1);
DeleteObject(hBmp2);
break;
case IDOK:
// 更换图片
{
HBITMAP tmp;
tmp = hBmp1;
hBmp1 = hBmp2;
hBmp2 = tmp;
SendDlgItemMessageA(hWnd, IDC_BMP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp1);
}
break;
case IDC_ONTOP:
// 设置是否总在最前面
if (IsDlgButtonChecked(hWnd, IDC_ONTOP) == BST_CHECKED)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
else
{
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
break;
case IDC_SHOWBMP:
// 演示隐藏和显示图片控件
{
HWND hDlg = GetDlgItem(hWnd, IDC_BMP);
if (IsWindowVisible(hDlg))
{
ShowWindow(hDlg, SW_HIDE);
}
else
{
ShowWindow(hDlg, SW_SHOW);
}
break;
}
case IDC_ALOW:
// 允许或灰化“更换图片”按钮
EnableWindow(GetDlgItem(hWnd, IDOK), IsDlgButtonChecked(hWnd, IDC_ALOW) == BST_CHECKED);
break;
case IDC_MODALFRAME:
SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) & (~WS_THICKFRAME));
break;
case IDC_THICKFRAME:
SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) | WS_THICKFRAME);
break;
case IDC_TITLETEXT:
// 演示处理下拉式组合框
if ((wParam >> 16) == CBN_SELENDOK)
{
DWORD retID = SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_GETCURSEL, 0, 0);
if (retID == 2)
{
EnableWindow(GetDlgItem(hWnd, IDC_CUSTOMTEXT), TRUE);
}
else
{
SendDlgItemMessageA(hWnd, IDC_TITLETEXT, CB_GETLBTEXT, retID, (LPARAM)szBuffer);
SetWindowTextA(hWnd, szBuffer);
EnableWindow(GetDlgItem(hWnd, IDC_CUSTOMTEXT), FALSE);
}
}
break;
case IDC_CUSTOMTEXT:
// 在文本框中输入文字
GetDlgItemTextA(hWnd, IDC_CUSTOMTEXT, szBuffer, sizeof(szBuffer));
SetWindowTextA(hWnd, szBuffer);
break;
}
break;
// WM_COMMAD消息的处理已经结束了
case WM_HSCROLL:
switch (wParam & 0xFFFF)
{
case SB_LINELEFT:
dwPos--;
break;
case SB_LINERIGHT:
dwPos++;
break;
case SB_PAGELEFT:
dwPos -= 10;
break;
case SB_PAGERIGHT:
dwPos += 10;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
dwPos = (wParam >> 16);
break;
default:
return TRUE;
}
if (dwPos < 0)dwPos = 0;
if (dwPos > 100)dwPos = 100;
SetDlgItemInt(hWnd, IDC_VALUE, dwPos, FALSE);
SendDlgItemMessageA(hWnd, IDC_SCROLL, SBM_SETPOS, dwPos, TRUE);
break;
default:
return FALSE;
}
return TRUE;
}
// 主函数,子系统选择windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInstance = GetModuleHandleA(NULL);
DialogBoxParamA(hInstance, (LPCSTR)DLG_MAIN, NULL, (DLGPROC)_ProcDlgMain, NULL);
return 0;
}1
2
3HWND WINAPI GetDlgItem(HWND hDlg,int nIDDlgItem); //通过控件ID获取其句柄
int WINAPI GetDlgCtrlID(HWND hWnd); //通过句柄获取ID
LRESULT WINAPI SendDlgItemMessageA(HWND hDlg,int nIDDlgItem,UINT Msg,WPARAM wParam,LPARAM lParam); //像某个对话框的控件发送消息1
2
3UINT WINAPI IsDlgButtonChecked(HWND hDlg,int nIDButton);
BOOL WINAPI CheckDlgButton(HWND hDlg,int nIDButton,UINT uCheck);
BOOL WINAPI CheckRadioButton(HWND hDlg,int nIDFirstButton,int nIDLastButton,int nIDCheckButton);
文本编辑框: 1
2
3
4
5UINT WINAPI GetDlgItemTextA(HWND hDlg,int nIDDlgItem,LPCSTR lpString,int cchMax);
BOOL WINAPI SetDlgItemTextA(HWND hDlg,int nIDDlgItem,LPCSTR lpString);
BOOL WINAPI SetDlgItemInt(HWND hDlg,int nIDDlgItem,UINT uValue,BOOL bSigned);
UINT WINAPI GetDlgItemInt(HWND hDlg,int nIDDlgItem,BOOL *lpTranslated,BOOL bSigned);
LRESULT WINAPI SendDlgItemMessageA(HWND hDlg,int nIDDlgItem, UINT Msg,WPARAM wParam,LPARAM lParam);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 消息处理分支,uMsg为WM_HSCROLL时
if(lParam == 滚动条的句柄1)
{
switch(wParam & 0xFFFF)
{
case SB_LINELEFT:
位置变量--;
break;
case SB_LINERIGHT:
位置变量++;
break;
case SB_PAGELEFT:
位置变量-=页长;
break;
case SB_PAGERIGHT:
位置变量+=页长;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
位置变量=(wParam>>16);
break;
}
}
else if(lParam == 滚动条的句柄2)
{
// ...
}1
2if (dwPos < 0)dwPos = 0;
if (dwPos > 100)dwPos = 100;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ICO_MAIN ICON "Main.ico"
DLG_MAIN DIALOG 163, 160, 190, 108
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "列表框控件实例"
FONT 9, "宋体"
{
LISTBOX IDC_LISTBOX1, 6, 5, 55, 86, LBS_STANDARD
LISTBOX IDC_LISTBOX2, 68, 5, 115, 86, LBS_STANDARD | LBS_MULTIPLESEL
LTEXT "", IDC_SEL1, 6, 93, 55, 8
PUSHBUTTON "复位(&R)", IDC_RESET, 89, 90, 45, 14
DEFPUSHBUTTON "查看(&S)", IDOK, 139, 90, 45, 14, WS_DISABLED
} 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
HINSTANCE hInstance;
LPCSTR szText1 = "项目1";
LPCSTR szText2 = "项目2";
LPCSTR szText3 = "项目3";
LPCSTR szPath = "*.*";
LPCSTR szMessage = "选择结果:%s";
LPCSTR szTitle = "您的选择";
LPCSTR szSelect = "您选择了以下的项目:";
LPCSTR szReturn = "\r\n";
LRESULT CALLBACK _ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DWORD szBuffer[128];
char szBuffer1[128];
char szTextBuff[2048];
int dwCount;
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, NULL);
break;
case WM_INITDIALOG:
SendMessageA(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconA(hInstance, (LPCSTR)ICO_MAIN));
//初始化列表框
SendDlgItemMessageA(hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, (LPARAM)"项目1");
SendDlgItemMessageA(hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, (LPARAM)"项目2");
SendDlgItemMessageA(hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, (LPARAM)"项目3");
SendDlgItemMessageA(hWnd, IDC_LISTBOX2, LB_DIR, DDL_ARCHIVE | DDL_DRIVES | DDL_DIRECTORY, (LPARAM)"*.*");
break;
case WM_COMMAND:
switch (wParam & 0xFFFF)
{
case IDOK:
{
dwCount = SendDlgItemMessageA(hWnd, IDC_LISTBOX2, LB_GETSELCOUNT, 0, 0);
SendDlgItemMessageA(hWnd, IDC_LISTBOX2, LB_GETSELITEMS, 128 / 4, (LPARAM)szBuffer);
strcpy(szTextBuff, szSelect);
for (int i = 0; i < dwCount; i++)
{
SendDlgItemMessageA(hWnd, IDC_LISTBOX2, LB_GETTEXT, szBuffer[i], (LPARAM)szBuffer1);
strcat(szTextBuff, "\r\n");
strcat(szTextBuff, szBuffer1);
}
MessageBoxA(hWnd, szTextBuff, szTitle, MB_OK);
}
break;
case IDC_RESET:
SendDlgItemMessageA(hWnd, IDC_LISTBOX2, LB_SETSEL, FALSE, -1);
break;
case IDC_LISTBOX1:
switch (wParam >> 16)
{
case LBN_SELCHANGE:
// 将鼠标点击结果显示在文本框中
{
UINT id = SendMessageA((HWND)lParam, LB_GETCURSEL, 0, 0);
SendMessageA((HWND)lParam, LB_GETTEXT, id, (LPARAM)szTextBuff);
SetDlgItemTextA(hWnd, IDC_SEL1, szTextBuff);
}
break;
case LBN_DBLCLK:
{
UINT id = SendMessageA((HWND)lParam, LB_GETCURSEL, 0, 0);
SendMessageA((HWND)lParam, LB_GETTEXT, id, (LPARAM)szTextBuff);
wsprintfA(szBuffer1, szMessage, szTextBuff);
MessageBoxA(hWnd, szBuffer1, szTitle, MB_OK);
}
break;
}
break;
case IDC_LISTBOX2:
if ((wParam >> 16) == LBN_SELCHANGE)
{
UINT id = SendMessageA((HWND)lParam, LB_GETSELCOUNT, 0, 0);
EnableWindow(GetDlgItem(hWnd, IDOK), id);
}
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
// 主函数,子系统选择windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInstance = GetModuleHandleA(NULL);
DialogBoxParamA(hInstance, (LPCSTR)DLG_MAIN, NULL, (DLGPROC)_ProcDlgMain, NULL);
return 0;
}