代码实现

新建空白工程

image-20240928165101566

在工程处新建菜单

image-20241005194813841

打开Res文件夹

image-20241005194916477

在文件名处输入MENU1后保存

image-20241005194942356

选择确定

image-20241005195034849

完成后资源处会增加菜单

image-20241005195107136

点击打开菜单编辑器然后输入标题选择插入然后确定

image-20241005195342597

插入成功后发现新建的菜单资源已成功写入

image-20241005195436740

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
.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib

IDR_MENU EQU 10000
.const
ASM_MENU_NAME db "51asmMenu",0
ASM_CLASS_NAME db "51asmClass",0
ASM_WND_NAME db "51asm",0

.code
ENTRY proc
LOCAL @msg:MSG
LOCAL @bRet:BOOL
LOCAL @wc:WNDCLASS
LOCAL @hInstance:HINSTANCE
LOCAL @hWnd:HWND

invoke GetModuleHandle,NULL
;check
mov @hInstance,eax

mov @wc.style,0
mov @wc.lpfnWndProc,offset WndProc
mov @wc.cbClsExtra,0
mov @wc.cbWndExtra,0
mov eax,@hInstance
mov @wc.hInstance,eax
invoke LoadIcon,NULL,IDI_APPLICATION
;check
mov @wc.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
;check
mov @wc.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
;check
mov @wc.hbrBackground,eax
mov @wc.lpszMenuName,IDR_MENU
mov @wc.lpszClassName,offset ASM_CLASS_NAME

invoke RegisterClass,addr @wc
;check

invoke CreateWindowEx,NULL,offset ASM_CLASS_NAME,offset ASM_WND_NAME,WS_OVERLAPPEDWINDOW,0,0,\
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,@hInstance,NULL
;check
mov @hWnd,eax

invoke ShowWindow,@hWnd,SW_SHOWNORMAL
;check

invoke UpdateWindow,@hWnd
;check
WHILE1:
invoke GetMessage,addr @msg,@hWnd,0,0
cmp eax,-1
jz WHILE1_END
cmp eax,0
jz WHILE1_END

invoke TranslateMessage,addr @msg
;check
invoke DispatchMessage,addr @msg
;check

jmp WHILE1
WHILE1_END:
invoke ExitProcess,0
ret
ENTRY endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
invoke PostQuitMessage,0
mov eax,TRUE
ret
.endif
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
WndProc endp
end ENTRY

运行结果

image-20241005195908358

在菜单下添加open,然后按右箭头表示在File的下级(此时前面会出现..)

image-20241005205800767

选择快捷键

image-20241005210325547

在工程中选中快捷键

image-20241005212308589

添加快捷键

image-20241005212233374

在工程中选择导入中的文件

image-20241005212718084

导入文件

image-20241005212648296

添加成功

image-20241005212347717

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
.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib

IDR_MENU EQU 10000
IDR_ACC EQU 1000
IDM_OPEN EQU 10002
.const
ASM_MENU_NAME db "51asmMenu",0
ASM_CLASS_NAME db "51asmClass",0
ASM_WND_NAME db "51asm",0

.code
ENTRY proc
LOCAL @msg:MSG
LOCAL @bRet:BOOL
LOCAL @wc:WNDCLASS
LOCAL @hInstance:HINSTANCE
LOCAL @hWnd:HWND
LOCAL @hAcc:HINSTANCE

invoke GetModuleHandle,NULL
;check
mov @hInstance,eax

mov @wc.style,0
mov @wc.lpfnWndProc,offset WndProc
mov @wc.cbClsExtra,0
mov @wc.cbWndExtra,0
mov eax,@hInstance
mov @wc.hInstance,eax
invoke LoadIcon,NULL,IDI_APPLICATION
;check
mov @wc.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
;check
mov @wc.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
;check
mov @wc.hbrBackground,eax
mov @wc.lpszMenuName,IDR_MENU
mov @wc.lpszClassName,offset ASM_CLASS_NAME

invoke RegisterClass,addr @wc
;check

invoke CreateWindowEx,NULL,offset ASM_CLASS_NAME,offset ASM_WND_NAME,WS_OVERLAPPEDWINDOW,0,0,\
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,@hInstance,NULL
;check
mov @hWnd,eax

invoke ShowWindow,@hWnd,SW_SHOWNORMAL
;check

invoke UpdateWindow,@hWnd
;check

invoke LoadAccelerators,@hInstance,IDR_ACC
mov @hAcc,eax
;check
WHILE1:
invoke GetMessage,addr @msg,@hWnd,0,0
cmp eax,-1
jz WHILE1_END
cmp eax,0
jz WHILE1_END

invoke TranslateAccelerator,@hWnd,@hAcc,addr @msg
.if !eax
invoke TranslateMessage,addr @msg
;check
invoke DispatchMessage,addr @msg
;check
.endif

jmp WHILE1
WHILE1_END:
invoke ExitProcess,0
ret
ENTRY endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
invoke PostQuitMessage,0
mov eax,TRUE
ret
.elseif uMsg==WM_COMMAND
mov eax,wParam
.if ax==IDM_OPEN
invoke MessageBox,NULL,NULL,NULL,MB_OK
mov eax,TRUE
ret
.endif
.endif
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
WndProc endp
end ENTRY

运行结果

image-20241005212455326

直接按快捷键Ctrl+O即可运行Open

image-20241005212535040

计算机完整代码

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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include msvcrt.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib msvcrt.lib

IDR_MENU EQU 10000
IDR_ACC EQU 10001
IDM_OPEN EQU 10002

.const
ASM_MENU_NAME db "51asmMenu",0
ASM_CLASS_NAME db "51asmClass",0
ASM_WND_NAME db "51asm",0
ASM_BUTTON_NAME db "BUTTON",0
ASM_EDIT_NAME db "EDIT",0
ASM_FORMAT db "%d",0
ASM_OPERATOR1 db "+",0
ASM_OPERATOR2 db "=",0
ASM_EDIT dd 30000

.data
g_hInstance dd 0
ASM_OP1 dd 0

.code
ENTRY proc
LOCAL @msg:MSG
LOCAL @bRet:BOOL
LOCAL @wc:WNDCLASS
LOCAL @hInstance:HINSTANCE
LOCAL @hWnd:HWND
LOCAL @hAcc:HINSTANCE

invoke GetModuleHandle,NULL
;check
mov @hInstance,eax
mov g_hInstance,eax

mov @wc.style,0
mov @wc.lpfnWndProc,offset WndProc
mov @wc.cbClsExtra,0
mov @wc.cbWndExtra,0
mov eax,@hInstance
mov @wc.hInstance,eax
invoke LoadIcon,NULL,IDI_APPLICATION
;check
mov @wc.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
;check
mov @wc.hCursor,eax
;invoke GetStockObject,WHITE_BRUSH
;check
mov @wc.hbrBackground,COLOR_WINDOW
mov @wc.lpszMenuName,IDR_MENU
mov @wc.lpszClassName,offset ASM_CLASS_NAME

invoke RegisterClass,addr @wc
;check

invoke CreateWindowEx,NULL,offset ASM_CLASS_NAME,offset ASM_WND_NAME,WS_OVERLAPPEDWINDOW,0,0,\
4*64,6*64,NULL,NULL,@hInstance,NULL
;check
mov @hWnd,eax

invoke ShowWindow,@hWnd,SW_SHOWNORMAL
;check

invoke UpdateWindow,@hWnd
;check

invoke LoadAccelerators,@hInstance,IDR_ACC
mov @hAcc,eax
;check
WHILE1:
invoke GetMessage,addr @msg,@hWnd,0,0
cmp eax,-1
jz WHILE1_END
cmp eax,0
jz WHILE1_END

invoke TranslateAccelerator,@hWnd,@hAcc,addr @msg
.if !eax
invoke TranslateMessage,addr @msg
;check
invoke DispatchMessage,addr @msg
;check
.endif

jmp WHILE1
WHILE1_END:
invoke ExitProcess,0
ret
ENTRY endp
OnClose proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
invoke DestroyWindow,hWnd
mov eax,TRUE
ret
OnClose endp
OnDestroy proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
invoke PostQuitMessage,0
mov eax,TRUE
ret
OnDestroy endp
OnOpen proc
invoke MessageBox,NULL,NULL,NULL,MB_OK
ret
OnOpen endp
OnCommand proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
mov eax,wParam
.if ax==IDM_OPEN
invoke OnOpen
mov eax,TRUE
ret
.elseif (ax>=20000)&&(ax<=20009)
sub ax,20000
movzx ebx,ax
invoke SetDlgItemInt,hWnd,ASM_EDIT,ebx,FALSE
mov eax,TRUE
ret
.elseif ax==20010 ;+
invoke GetDlgItemInt,hWnd,ASM_EDIT,NULL,FALSE
mov ASM_OP1,eax
invoke SetDlgItemText,hWnd,ASM_EDIT,NULL
mov eax,TRUE
ret
.elseif ax==20011 ;=
invoke GetDlgItemInt,hWnd,ASM_EDIT,NULL,FALSE
mov ebx,ASM_OP1
add ebx,eax
invoke SetDlgItemInt,hWnd,ASM_EDIT,ebx,FALSE
mov eax,TRUE
ret
.endif

mov eax,FALSE
ret
OnCommand endp
OnCreate proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL @row:dword
LOCAL @col:dword
LOCAL @buf[260]:byte
LOCAL @num:dword
LOCAL @x:dword
LOCAL @y:dword
LOCAL @id:dword
mov @id,20000
mov @x,0
mov @y,0
mov @num,0

mov @row,0
.while @row<3
mov @col,0
.while @col<3
invoke crt_sprintf,addr @buf,offset ASM_FORMAT,@num
mov eax,@col
shl eax,6
mov @x,eax ;x=col*64

mov eax,@row
shl eax,6
mov @y,eax ;x=row*64

invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,addr @buf,\
WS_CHILD or WS_VISIBLE,@x,@y,64,64,hWnd,@id,g_hInstance,NULL
;check
inc @col
inc @num
inc @id
.endw
inc @row
.endw
;创建子窗口
invoke crt_sprintf,addr @buf,offset ASM_FORMAT,9
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,addr @buf,\
WS_CHILD or WS_VISIBLE,0*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,offset ASM_OPERATOR1,\
WS_CHILD or WS_VISIBLE,1*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,offset ASM_OPERATOR2,\
WS_CHILD or WS_VISIBLE,2*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_EDIT_NAME,NULL,\
WS_CHILD or WS_VISIBLE,0*64,4*64,3*64,64,hWnd,ASM_EDIT,g_hInstance,NULL
inc @id
mov eax,TRUE
ret
OnCreate endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
invoke OnClose,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_DESTROY
invoke OnDestroy,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_CREATE
invoke OnCreate,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_COMMAND
invoke OnCommand,hWnd,uMsg,wParam,lParam
ret
.endif

invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
WndProc endp


end ENTRY

运行结果

image-20241005230721666

计算1+2

image-20241005230742681

代码分析

1.段落说明

1
2
3
.386
.model flat,stdcall
option casemap:none
  • **.386**:指定处理器架构为 80386。
  • **.model flat,stdcall**:平坦内存模型,使用 stdcall 调用约定。
  • **option casemap:none**:禁止大小写转换。

2. 引用 Windows API 和库

1
2
3
4
5
6
7
8
9
10
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include msvcrt.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib msvcrt.lib
  • include 和 **includelib**:包含必要的 Windows API 头文件和库,以支持程序的窗口、按钮、消息处理、内存管理等功能。

3. 常量定义

1
2
3
IDR_MENU  EQU 10000
IDR_ACC EQU 10001
IDM_OPEN EQU 10002
  • **EQU**:定义了菜单、加速器和命令的常量,表示唯一的资源 ID。
1
2
3
4
5
6
7
8
9
10
.const
ASM_MENU_NAME db "51asmMenu",0
ASM_CLASS_NAME db "51asmClass",0
ASM_WND_NAME db "51asm",0
ASM_BUTTON_NAME db "BUTTON",0
ASM_EDIT_NAME db "EDIT",0
ASM_FORMAT db "%d",0
ASM_OPERATOR1 db "+",0
ASM_OPERATOR2 db "=",0
ASM_EDIT dd 30000
  • .const 定义了多个常量字符串,用于窗口类名、按钮类名和其他 UI 元素的名称。
  • ASM_EDIT 定义了一个唯一的编辑框 ID(30000),用于标识编辑控件。

4. 数据段

1
2
3
.data
g_hInstance dd 0
ASM_OP1 dd 0
  • **.data**:定义了全局变量,g_hInstance 用于存储应用程序实例句柄,ASM_OP1 用于存储第一个操作数。

5. 程序入口 ENTRY

1
2
3
4
5
6
7
ENTRY proc
LOCAL @msg:MSG
LOCAL @bRet:BOOL
LOCAL @wc:WNDCLASS
LOCAL @hInstance:HINSTANCE
LOCAL @hWnd:HWND
LOCAL @hAcc:HINSTANCE
  • **ENTRY**:定义了程序的入口函数。函数内部定义了多个局部变量,使用 @ 前缀,比如 @msg@bRet@wc 等。这些变量用于窗口创建和消息循环的管理。
1
2
3
invoke GetModuleHandle,NULL
mov @hInstance,eax
mov g_hInstance,eax
  • **GetModuleHandle**:获取当前模块的句柄,并将其存储到局部变量 @hInstance 和全局变量 g_hInstance

6. 窗口类的注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mov @wc.style,0
mov @wc.lpfnWndProc,offset WndProc
mov @wc.cbClsExtra,0
mov @wc.cbWndExtra,0
mov eax,@hInstance
mov @wc.hInstance,eax
invoke LoadIcon,NULL,IDI_APPLICATION
mov @wc.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov @wc.hCursor,eax
mov @wc.hbrBackground,COLOR_WINDOW
mov @wc.lpszMenuName,IDR_MENU
mov @wc.lpszClassName,offset ASM_CLASS_NAME
invoke RegisterClass,addr @wc
  • 这里通过设置 WNDCLASS 结构的各个字段,注册了窗口类,包括窗口过程地址 WndProc、光标、背景色和菜单等信息。

7. 创建窗口

1
2
3
invoke CreateWindowEx,NULL,offset ASM_CLASS_NAME,offset ASM_WND_NAME,WS_OVERLAPPEDWINDOW,0,0,\
4*64,6*64,NULL,NULL,@hInstance,NULL
mov @hWnd,eax
  • **CreateWindowEx**:创建一个窗口,大小设置为 4*64 宽度和 6*64 高度。创建成功后,窗口句柄存储在 @hWnd 中。

8. 消息循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WHILE1:
invoke GetMessage,addr @msg,@hWnd,0,0
cmp eax,-1
jz WHILE1_END
cmp eax,0
jz WHILE1_END

invoke TranslateAccelerator,@hWnd,@hAcc,addr @msg
.if !eax
invoke TranslateMessage,addr @msg
invoke DispatchMessage,addr @msg
.endif

jmp WHILE1
WHILE1_END:
invoke ExitProcess,0
ret
ENTRY endp
  • 消息循环:不断调用 GetMessage,获取消息并分发给相应的窗口过程。循环一直运行,直到收到退出消息。

9. 消息处理函数 WndProc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
invoke OnClose,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_DESTROY
invoke OnDestroy,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_CREATE
invoke OnCreate,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_COMMAND
invoke OnCommand,hWnd,uMsg,wParam,lParam
ret
.endif

invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
WndProc endp
  • **WndProc**:窗口过程处理不同的消息,比如 WM_CREATEWM_CLOSEWM_COMMAND 等,通过调用相应的函数(如 OnCreateOnClose)处理消息。

10. 创建按钮和编辑框(OnCreate

1
2
3
4
5
6
7
8
9
10
11
12
OnCreate proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL @row:dword
LOCAL @col:dword
LOCAL @buf[260]:byte
LOCAL @num:dword
LOCAL @x:dword
LOCAL @y:dword
LOCAL @id:dword
mov @id,20000
mov @x,0
mov @y,0
mov @num,0
  • **OnCreate**:在窗口创建时调用,创建按钮和编辑框。
  • **@id**:从 20000 开始递增,用于给每个按钮分配一个唯一的 ID。
  • **@x@y**:用于计算按钮的坐标位置。
  • 循环创建按钮:每个按钮的数字显示为 @num,并通过 CreateWindowEx 函数动态创建子窗口(按钮)。

我们继续从上次的地方逐行分析代码。接下来,我们着重分析与窗口创建和消息处理相关的部分。

11. OnCommand 处理不同的命令消息

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
OnCommand proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
mov eax,wParam
.if ax==IDM_OPEN
invoke OnOpen
mov eax,TRUE
ret
.elseif (ax>=20000)&&(ax<=20009)
sub ax,20000
movzx ebx,ax
invoke SetDlgItemInt,hWnd,ASM_EDIT,ebx,FALSE
mov eax,TRUE
ret
.elseif ax==20010 ;+
invoke GetDlgItemInt,hWnd,ASM_EDIT,NULL,FALSE
mov ASM_OP1,eax
invoke SetDlgItemText,hWnd,ASM_EDIT,NULL
mov eax,TRUE
ret
.elseif ax==20011 ;=
invoke GetDlgItemInt,hWnd,ASM_EDIT,NULL,FALSE
mov ebx,ASM_OP1
add ebx,eax
invoke SetDlgItemInt,hWnd,ASM_EDIT,ebx,FALSE
mov eax,TRUE
ret
.endif

mov eax,FALSE
ret
OnCommand endp
  • OnCommand 函数根据传递的 wParam 来确定用户点击的按钮,并执行相应的操作:
    • **ax == IDM_OPEN**:如果命令是打开操作(IDM_OPEN),则调用 OnOpen 函数并显示对话框。
    • **ax >= 20000 && ax <= 20009**:如果用户按了数字按钮,则将数字写入编辑框 (ASM_EDIT) 中。wParam 包含按钮的 ID,这里通过减去 20000 获得按钮编号,并通过 SetDlgItemInt 函数将该值显示在编辑框内。
    • ax == 20010(即 + 按钮):点击加号按钮时,获取当前编辑框中的数值,并将其保存在 ASM_OP1 中。然后清空编辑框的内容,准备下一个操作数的输入。
    • ax == 20011(即 = 按钮):当按下等号按钮时,获取当前编辑框中的数值并与 ASM_OP1 的值相加,然后将结果显示在编辑框中。

12. 创建窗口子元素 OnCreate

1
2
3
4
5
6
7
8
9
10
11
12
OnCreate proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL @row:dword
LOCAL @col:dword
LOCAL @buf[260]:byte
LOCAL @num:dword
LOCAL @x:dword
LOCAL @y:dword
LOCAL @id:dword
mov @id,20000
mov @x,0
mov @y,0
mov @num,0
  • **OnCreate**:当窗口被创建时,会调用此函数来创建子窗口元素,比如按钮和编辑框。这里定义了一些局部变量用于控制窗口布局:
    • **@id**:从 20000 开始,用于分配给按钮的唯一 ID
    • **@x@y**:用于指定每个按钮的横纵坐标。
    • **@num**:用于跟踪按钮上显示的数字。

创建按钮和编辑框的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mov @row,0
.while @row<3
mov @col,0
.while @col<3
invoke crt_sprintf,addr @buf,offset ASM_FORMAT,@num
mov eax,@col
shl eax,6
mov @x,eax ;x=col*64

mov eax,@row
shl eax,6
mov @y,eax ;x=row*64

invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,addr @buf,\
WS_CHILD or WS_VISIBLE,@x,@y,64,64,hWnd,@id,g_hInstance,NULL
inc @col
inc @num
inc @id
.endw
inc @row
.endw
  • 循环创建按钮:外层循环控制行(@row),内层循环控制列(@col),每次循环都生成一个按钮。按钮的文本内容是数字 @num,使用 sprintf 格式化到 @buf 中。
  • 按钮位置:按钮的 x 坐标是 @col * 64y 坐标是 @row * 64,即每个按钮的大小为 64x64 像素。
  • 创建按钮CreateWindowEx 函数创建一个按钮,每个按钮都有唯一的 ID,从 20000 开始递增。

创建最后一行的按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
invoke crt_sprintf,addr @buf,offset ASM_FORMAT,9
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,addr @buf,\
WS_CHILD or WS_VISIBLE,0*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,offset ASM_OPERATOR1,\
WS_CHILD or WS_VISIBLE,1*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_BUTTON_NAME,offset ASM_OPERATOR2,\
WS_CHILD or WS_VISIBLE,2*64,3*64,64,64,hWnd,@id,g_hInstance,NULL
inc @id
invoke CreateWindowEx,NULL,offset ASM_EDIT_NAME,NULL,\
WS_CHILD or WS_VISIBLE,0*64,4*64,3*64,64,hWnd,ASM_EDIT,g_hInstance,NULL
inc @id
  • 9 按钮:在最后一行第一个位置创建了数字 9 的按钮。
  • += 按钮:在第二和第三个位置分别创建了加号和等号按钮。
  • 编辑框:在最后一行创建了一个编辑框,占用了整行的宽度。

13. 消息处理函数 WndProc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
invoke OnClose,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_DESTROY
invoke OnDestroy,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_CREATE
invoke OnCreate,hWnd,uMsg,wParam,lParam
ret
.elseif uMsg==WM_COMMAND
invoke OnCommand,hWnd,uMsg,wParam,lParam
ret
.endif

invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
WndProc endp
  • WndProc 是窗口过程函数,负责处理各种窗口消息,比如:
    • **WM_CLOSE**:当窗口关闭时,调用 OnClose 函数,销毁窗口。
    • **WM_DESTROY**:当窗口被销毁时,调用 OnDestroy,发送退出消息。
    • **WM_CREATE**:当窗口创建时,调用 OnCreate 来生成按钮和编辑框。
    • **WM_COMMAND**:当用户点击按钮或选择菜单项时,调用 OnCommand 进行处理。
  • 如果没有特殊处理的消息,调用默认的窗口过程 DefWindowProc 进行处理。