windows10 安装NASM

在编写nasm时数字默认为10进制

x86控制台程序

objexe.asm:

; equ 定义常量
STD_OUTPUT_HANDLE EQU -11

; 导入外部符号, Windows API函数,已修饰
extern _GetStdHandle@4              
extern _WriteFile@20
extern _ExitProcess@4

global Start                                    ; 导出符号。 入口点

section .data                                   ; 初始化数据段
 Message        db "hello.", 0Dh, 0Ah           ; 0Dh 0Ah 回车/换行
 MessageLength  EQU $ - Message                 ; $ - Message地址 == Message长度

section .bss                                    ; 未初始化的数据段
 Written        resd 1                          ; 一个dword的大小,4字节,32位

section .text                                   ; 代码段
Start:
 push  STD_OUTPUT_HANDLE
 call  _GetStdHandle@4				;检索指定标准设备

 push  0                                        ; 第5个参数
 push  Written                                  ; 第4个参数
 push  MessageLength                            ; 第3个参数
 push  Message                                  ; 第2个参数
 push  EAX                        		; 第1个参数 i/o句柄
 call  _WriteFile@20                            ; 调用函数,将数据写入指定的文件或输入/输出(I/O)设备
 
 push  0
 call  _ExitProcess@4
>nasm -f win32 objexe.asm
>golink /entry:Start /console kernel32.dll user32.dll objexe.obj
>objexe.exe
hello.

x64控制台程序

STD_OUTPUT_HANDLE EQU -11

extern GetStdHandle
extern WriteFile
extern ExitProcess

global Start

section .data
 Message        db "hello x64.", 0Dh, 0Ah
 MessageLength  EQU $-Message

section .bss
; 结构对齐
; 最大成员为8字节
; 该结构应从8整除的内存位置开始,alignb 8确保
; 如果其中有个成员为4字节,那么你要保证接下来的8字节成员自然对齐,那么就需要加padding
; 最后: 总长度要可以被8整除,不能整除就加padding 16/8=2
alignb 8
 StandardHandle resq 1
 Written        resq 1
 
 ; test resd 1 ;4字节成员
 ; padding1 resd 1 ;加padding对齐

section .text
Start:
 sub   RSP, 8                                   ; 将堆栈对齐为16个字节的倍数

 sub   RSP, 32                                  ; 阴影空间32字节
 mov   ECX, STD_OUTPUT_HANDLE
 call  GetStdHandle
 
 mov   qword [REL StandardHandle], RAX
 add   RSP, 32                                  ; 删除32个字节

 ; 4个参数每个8字节就是32,外加一个堆栈上的就是8字节
 ; 函数在内部会把这些参数放在堆栈,寄存器就那么几个
 ; 最后+8使堆栈对齐16个字节的倍数 48/16=3  40/16=2.5,能整除就是对齐
 sub   RSP, 32 + 8 + 8                          ; 阴影空间+第5个参数+对齐堆栈
                                                ; to a multiple of 16 bytes
 mov   RCX, qword [REL StandardHandle]          ; 第1个参数
 lea   RDX, [REL Message]                       ; 第2个参数
 mov   R8, MessageLength                        ; 第3个参数
 lea   R9, [REL Written]                        ; 第4个参数
 mov   qword [RSP + 32], 0                   	; 第5个参数
 call  WriteFile
 add   RSP, 48                                  ; 删除48个字节

 xor   ECX, ECX									;异或 ECX = 0
 call  ExitProcess
>nasm -f win64 objexe.asm
>golink /entry:Start /console kernel32.dll user32.dll objexe.obj
>objexe.exe
hello x64.

x86 MessageBox

extern MessageBoxA
extern ExitProcess

global Start

section .data
	text db "hello.",0
	title db "title",0
	
section .text
Start:
	; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
	push 0x00000001
	push title
	push text
	push 0
	call MessageBoxA
	
	; 如果点击了取消,就继续弹
	cmp eax,2
	je Start
	
	; 退出
	push 0
	call ExitProcess
>nasm -f win32 objexe.asm
>golink /entry:Start kernel32.dll user32.dll objexe.obj
>objexe.exe

x64 MessageBox

extern MessageBoxA
extern ExitProcess

global Start

section .data
	text db "hello.",0
	title db "title",0
	
section .text
Start:
	sub rsp, 8
	sub rsp, 32

_msgbox:
	; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
	xor ecx,ecx
	lea edx,[text]
	lea r8,[title]
	mov r9,0x01
	call MessageBoxA

	; 如果点击了取消,就继续弹
	cmp rax,0x2
	je _msgbox
	
	add rsp, 32

_exit:
	xor ecx,ecx
	call ExitProcess
>nasm -f win64 objexe.asm
>golink /entry:Start kernel32.dll user32.dll objexe.obj
>objexe.exe

x86 基本窗口

WindowWidth         EQU 640
WindowHeight        EQU 480

extern _CreateWindowExA@48
extern _DefWindowProcA@16
extern _DispatchMessageA@4
extern _ExitProcess@4
extern _GetMessageA@16
extern _GetModuleHandleA@4
extern _IsDialogMessageA@8
extern _LoadImageA@24
extern _PostQuitMessage@4
extern _RegisterClassExA@4
extern _ShowWindow@8
extern _TranslateMessage@4
extern _UpdateWindow@4

global Start

section .data
 WindowName db "Basic Window 32", 0
 ClassName  db "Window", 0

section .bss
 hInstance resd 1

section .text
Start:

 ; https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
 push  0
 call  _GetModuleHandleA@4
 mov   dword [hInstance], EAX ; 保存返回的模块句柄

 call  WinMain

.Exit:
 push  0
 call  _ExitProcess@4

WinMain:
 push  EBP
 mov   EBP, ESP
 sub   ESP, 80                                  ; 80个字节的局部变量的空间

; define 指令用于分配存储空间。它可以用于保留以及初始化一个或多个字节。
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa
%define wc                 EBP - 80             ; WNDCLASSEX结构(包含窗口类信息). 48字节
%define wc.cbSize          EBP - 80
%define wc.style           EBP - 76
%define wc.lpfnWndProc     EBP - 72
%define wc.cbClsExtra      EBP - 68
%define wc.cbWndExtra      EBP - 64
%define wc.hInstance       EBP - 60
%define wc.hIcon           EBP - 56
%define wc.hCursor         EBP - 52
%define wc.hbrBackground   EBP - 48
%define wc.lpszMenuName    EBP - 44
%define wc.lpszClassName   EBP - 40
%define wc.hIconSm         EBP - 36

%define msg                EBP - 32             ; MSG结构. 28字节
%define msg.hwnd           EBP - 32             ; 不必拆分每个成员
%define msg.message        EBP - 28             ; 在这种情况下,但它显示了每个
%define msg.wParam         EBP - 24             ; 成员在堆栈上
%define msg.lParam         EBP - 20
%define msg.time           EBP - 16
%define msg.pt.x           EBP - 12
%define msg.pt.y           EBP - 8

%define hWnd               EBP - 4

 mov   dword [wc.cbSize], 48                    ; [EBP - 80]
 mov   dword [wc.style], 2 | 1 | 2000h  		; [EBP - 76]
 mov   dword [wc.lpfnWndProc], WndProc          ; [EBP - 72]
 mov   dword [wc.cbClsExtra], 0              ; [EBP - 68]
 mov   dword [wc.cbWndExtra], 0              ; [EBP - 64]
 mov   EAX, dword [hInstance]                   ; Global
 mov   dword [wc.hInstance], EAX                ; [EBP - 60]

 ; 加载图标,光标,动画光标或位图
 push  8000h
 push  0
 push  0
 push  1	;加载一个图标
 push  7F00h	
 push  0
 call  _LoadImageA@24                           ; Large program icon
 mov   dword [wc.hIcon], EAX                    ; [EBP - 56]

 push  8000h
 push  0
 push  0
 push  2	;加载游标
 push  7F00h
 push  0
 call  _LoadImageA@24                           ; Cursor
 mov   dword [wc.hCursor], EAX                  ; [EBP - 52]

 mov   dword [wc.hbrBackground], 5 + 1  ; [EBP - 48]
 mov   dword [wc.lpszMenuName], 0            ; [EBP - 44]
 mov   dword [wc.lpszClassName], ClassName      ; [EBP - 40]

 push  8000h
 push  0
 push  0
 push  1
 push  7F00h
 push  0
 call  _LoadImageA@24                           ; Small program icon
 mov   dword [wc.hIconSm], EAX                  ; [EBP - 36]

 ; 注册一个窗口类,供以后在对CreateWindow或CreateWindowEx函数的调用中使用
 ;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexa
 lea   EAX, [wc]                                ; [EBP - 80]
 push  EAX
 call  _RegisterClassExA@4

;创建具有扩展窗口样式的重叠窗口
;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
 push  0
 push  dword [hInstance]                        ; Global
 push  0
 push  0
 push  WindowHeight
 push  WindowWidth
 push  0	;y
 push  0	;x
 push  0CF0000h		;窗口样式 https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
 push  WindowName                               ; Global
 push  ClassName                                ; Global
 push  2000000h
 call  _CreateWindowExA@48
 mov   dword [hWnd], EAX                        ; [EBP - 4] 新窗口句柄

;设置指定窗口的显示状态, 1 激活并显示一个窗口
 push  1
 push  dword [hWnd]                             ; [EBP - 4]
 call  _ShowWindow@8

 push  dword [hWnd]                             ; [EBP - 4]
 call  _UpdateWindow@4

.MessageLoop:

;从调用线程的消息队列中检索消息
 lea   EAX, [msg]                               ; [EBP - 32]
 push  0
 push  0
 push  0
 push  EAX
 call  _GetMessageA@16
 cmp   EAX, 0
 je    .Done

;确定是否将消息用于指定的对话框,如果是,则处理该消息
 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 push  dword [hWnd]                             ; [EBP - 4]
 call  _IsDialogMessageA@8                      ; For keyboard strokes
 cmp   EAX, 0									; 如果消息已处理,则返回值为非零
 jne   .MessageLoop                             ; 跳过TranslateMessage和DispatchMessage

;将虚拟键消息转换为字符消息。字符消息将发布到调用线程的消息队列中
;以在线程下次调用GetMessage或PeekMessage函数时读取。
 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 call  _TranslateMessage@4

;将消息调度到窗口过程。它通常用于调度由GetMessage函数检索的消息
 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 call  _DispatchMessageA@4
 jmp   .MessageLoop

.Done:
 mov   ESP, EBP                                 ; Remove the stack frame
 pop   EBP
 xor   EAX, EAX
 ret

; 回调函数
WndProc:
 push  EBP                                      ; Set up a Stack frame
 mov   EBP, ESP

; 获取参数
%define hWnd    EBP + 8                         ; Location of the 4 passed parameters from
%define uMsg    EBP + 12                        ; the calling function
%define wParam  EBP + 16                        ; We can now access these parameters by name
%define lParam  EBP + 20

 cmp   dword [uMsg], 2                 ; [EBP + 12]
 je    _WMDESTROY

_DefaultMessage:
 push  dword [lParam]                           ; [EBP + 20]
 push  dword [wParam]                           ; [EBP + 16]
 push  dword [uMsg]                             ; [EBP + 12]
 push  dword [hWnd]                             ; [EBP + 8]
 call  _DefWindowProcA@16

 mov   ESP, EBP                                 ; Remove the stack frame
 pop   EBP
 ret   16                                       ; 内平栈,这就涉及到调用约定,被调用的函数需要处理堆栈

_WMDESTROY:
;向系统指示线程已请求终止(退出)
 push  0
 call  _PostQuitMessage@4

 xor   EAX, EAX                                 ; return 0
 mov   ESP, EBP                                 ; Remove the stack frame
 pop   EBP
 ret   16                                       ; 内平栈
>nasm -f win32 objexe.asm
>golink /entry:Start kernel32.dll user32.dll  objexe.obj
>objexe.exe

x64 基本窗口

                                                ; Basic Window, 64 bit. V1.02
COLOR_WINDOW        EQU 5                       ; Constants
CS_BYTEALIGNWINDOW  EQU 2000h
CS_HREDRAW          EQU 2
CS_VREDRAW          EQU 1
CW_USEDEFAULT       EQU 80000000h
IDC_ARROW           EQU 7F00h
IDI_APPLICATION     EQU 7F00h
IMAGE_CURSOR        EQU 2
IMAGE_ICON          EQU 1
LR_SHARED           EQU 8000h
NULL                EQU 0
SW_SHOWNORMAL       EQU 1
WM_DESTROY          EQU 2
WS_EX_COMPOSITED    EQU 2000000h
WS_OVERLAPPEDWINDOW EQU 0CF0000h

WindowWidth         EQU 640
WindowHeight        EQU 480

extern CreateWindowExA                          ; Import external symbols
extern DefWindowProcA                           ; Windows API functions, not decorated
extern DispatchMessageA
extern ExitProcess
extern GetMessageA
extern GetModuleHandleA
extern IsDialogMessageA
extern LoadImageA
extern PostQuitMessage
extern RegisterClassExA
extern ShowWindow
extern TranslateMessage
extern UpdateWindow

global Start                                    ; Export symbols. The entry point

section .data                                   ; Initialized data segment
 WindowName  db "Basic Window 64", 0
 ClassName   db "Window", 0

section .bss                                    ; Uninitialized data segment
 alignb 8
 hInstance resq 1

section .text                                   ; Code segment
Start:
 sub   RSP, 8                                   ; Align stack pointer to 16 bytes

 ; GetModuleHandleA 只有一个参数为什么还分配32字节的阴影空间
 ; win64函数约定,假定您已经分配了32个字节的堆栈空间来存储四个参数寄存器
 ; 自己写的函数可以不用遵守
 sub   RSP, 32                                  ; 32 bytes of shadow space
 xor   ECX, ECX
 call  GetModuleHandleA
 mov   qword [REL hInstance], RAX
 add   RSP, 32                                  ; Remove the 32 bytes

 call  WinMain

.Exit:
 xor   ECX, ECX
 call  ExitProcess

WinMain:
 push  RBP                                      ; Set up a stack frame
 mov   RBP, RSP
 sub   RSP, 136 + 8                             ; 局部变量为136个字节
                                                ; a multiple of 16 (for Windows API functions),
                                                ; the + 8 takes care of this.

%define wc                 RBP - 136            ; WNDCLASSEX structure, 80 bytes
%define wc.cbSize          RBP - 136            ; 4 bytes. Start on an 8 byte boundary
%define wc.style           RBP - 132            ; 4 bytes
%define wc.lpfnWndProc     RBP - 128            ; 8 bytes
%define wc.cbClsExtra      RBP - 120            ; 4 bytes
%define wc.cbWndExtra      RBP - 116            ; 4 bytes
%define wc.hInstance       RBP - 112            ; 8 bytes
%define wc.hIcon           RBP - 104            ; 8 bytes
%define wc.hCursor         RBP - 96             ; 8 bytes
%define wc.hbrBackground   RBP - 88             ; 8 bytes
%define wc.lpszMenuName    RBP - 80             ; 8 bytes
%define wc.lpszClassName   RBP - 72             ; 8 bytes
%define wc.hIconSm         RBP - 64             ; 8 bytes. End on an 8 byte boundary

; msg结构最大成员8字节 总字节数44+4字节的结构对齐=48 48/8=6 
%define msg                RBP - 56             ; MSG structure, 48 bytes
%define msg.hwnd           RBP - 56             ; 8 bytes. Start on an 8 byte boundary
%define msg.message        RBP - 48             ; 4 bytes
%define msg.Padding1       RBP - 44             ; 4字节自然对齐
%define msg.wParam         RBP - 40             ; 8 bytes
%define msg.lParam         RBP - 32             ; 8 bytes
%define msg.time           RBP - 24             ; 4 bytes
%define msg.py.x           RBP - 20             ; 4 bytes
%define msg.pt.y           RBP - 16             ; 4 bytes
%define msg.Padding2       RBP - 12             ; 4字节结构对齐

%define hWnd               RBP - 8              ; 8 bytes

 mov   dword [wc.cbSize], 80                    ; [RBP - 136]
 mov   dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW  ; [RBP - 132]
 lea   RAX, [REL WndProc]
 mov   qword [wc.lpfnWndProc], RAX              ; [RBP - 128]
 mov   dword [wc.cbClsExtra], NULL              ; [RBP - 120]
 mov   dword [wc.cbWndExtra], NULL              ; [RBP - 116]
 mov   RAX, qword [REL hInstance]               ; Global
 mov   qword [wc.hInstance], RAX                ; [RBP - 112]

 sub   RSP, 32 + 16                             ; Shadow space + 2 parameters
 xor   ECX, ECX
 mov   EDX, IDI_APPLICATION
 mov   R8D, IMAGE_ICON
 xor   R9D, R9D
 mov   qword [RSP + 4 * 8], NULL
 mov   qword [RSP + 5 * 8], LR_SHARED
 call  LoadImageA                               ; Large program icon
 mov   qword [wc.hIcon], RAX                    ; [RBP - 104]
 add   RSP, 48                                  ; Remove the 48 bytes

 sub   RSP, 32 + 16                             ; Shadow space + 2 parameters
 xor   ECX, ECX
 mov   EDX, IDC_ARROW
 mov   R8D, IMAGE_CURSOR
 xor   R9D, R9D
 mov   qword [RSP + 4 * 8], NULL
 mov   qword [RSP + 5 * 8], LR_SHARED
 call  LoadImageA                               ; Cursor
 mov   qword [wc.hCursor], RAX                  ; [RBP - 96]
 add   RSP, 48                                  ; Remove the 48 bytes

 mov   qword [wc.hbrBackground], COLOR_WINDOW + 1  ; [RBP - 88]
 mov   qword [wc.lpszMenuName], NULL            ; [RBP - 80]
 lea   RAX, [REL ClassName]
 mov   qword [wc.lpszClassName], RAX            ; [RBP - 72]

 sub   RSP, 32 + 16                             ; Shadow space + 2 parameters
 xor   ECX, ECX
 mov   EDX, IDI_APPLICATION
 mov   R8D, IMAGE_ICON
 xor   R9D, R9D
 mov   qword [RSP + 4 * 8], NULL
 mov   qword [RSP + 5 * 8], LR_SHARED
 call  LoadImageA                               ; Small program icon
 mov   qword [wc.hIconSm], RAX                  ; [RBP - 64]
 add   RSP, 48                                  ; Remove the 48 bytes

 sub   RSP, 32                                  ; 32 bytes of shadow space
 lea   RCX, [wc]                                ; [RBP - 136]
 call  RegisterClassExA
 add   RSP, 32                                  ; Remove the 32 bytes

 sub   RSP, 32 + 64                             ; Shadow space + 8 parameters
 mov   ECX, WS_EX_COMPOSITED
 lea   RDX, [REL ClassName]                     ; Global
 lea   R8, [REL WindowName]                     ; Global
 mov   R9D, WS_OVERLAPPEDWINDOW
 mov   dword [RSP + 4 * 8], CW_USEDEFAULT
 mov   dword [RSP + 5 * 8], CW_USEDEFAULT
 mov   dword [RSP + 6 * 8], WindowWidth
 mov   dword [RSP + 7 * 8], WindowHeight
 mov   qword [RSP + 8 * 8], NULL
 mov   qword [RSP + 9 * 8], NULL
 mov   RAX, qword [REL hInstance]               ; Global
 mov   qword [RSP + 10 * 8], RAX
 mov   qword [RSP + 11 * 8], NULL
 call  CreateWindowExA
 mov   qword [hWnd], RAX                        ; [RBP - 8]
 add   RSP, 96                                  ; Remove the 96 bytes

 sub   RSP, 32                                  ; 32 bytes of shadow space
 mov   RCX, qword [hWnd]                        ; [RBP - 8]
 mov   EDX, SW_SHOWNORMAL
 call  ShowWindow
 add   RSP, 32                                  ; Remove the 32 bytes

 sub   RSP, 32                                  ; 32 bytes of shadow space
 mov   RCX, qword [hWnd]                        ; [RBP - 8]
 call  UpdateWindow
 add   RSP, 32                                  ; Remove the 32 bytes

.MessageLoop:
 sub   RSP, 32                                  ; 32 bytes of shadow space
 lea   RCX, [msg]                               ; [RBP - 56]
 xor   EDX, EDX
 xor   R8D, R8D
 xor   R9D, R9D
 call  GetMessageA
 add   RSP, 32                                  ; Remove the 32 bytes
 cmp   RAX, 0
 je    .Done

 sub   RSP, 32                                  ; 32 bytes of shadow space
 mov   RCX, qword [hWnd]                        ; [RBP - 8]
 lea   RDX, [msg]                               ; [RBP - 56]
 call  IsDialogMessageA                         ; For keyboard strokes
 add   RSP, 32                                  ; Remove the 32 bytes
 cmp   RAX, 0
 jne   .MessageLoop                             ; Skip TranslateMessage and DispatchMessageA

 sub   RSP, 32                                  ; 32 bytes of shadow space
 lea   RCX, [msg]                               ; [RBP - 56]
 call  TranslateMessage
 add   RSP, 32                                  ; Remove the 32 bytes

 sub   RSP, 32                                  ; 32 bytes of shadow space
 lea   RCX, [msg]                               ; [RBP - 56]
 call  DispatchMessageA
 add   RSP, 32                                  ; Remove the 32 bytes
 jmp   .MessageLoop

.Done:
 mov   RSP, RBP                                 ; Remove the stack frame
 pop   RBP
 xor   EAX, EAX
 ret

; 回调函数
WndProc:
 push  RBP                                      ; Set up a stack frame
 mov   RBP, RSP

; 获取阴影空间
; call+上面的push  RBP会导致rsp-16,所以第一个要+16
%define hWnd   RBP + 16                         ; Location of the shadow space setup by
%define uMsg   RBP + 24                         ; the calling function
%define wParam RBP + 32
%define lParam RBP + 40

; 将函数参数放在阴影空间
 mov   qword [hWnd], RCX                        ; Free up RCX RDX R8 R9 by spilling the
 mov   qword [uMsg], RDX                        ; 4 passed parameters to the shadow space
 mov   qword [wParam], R8                       ; We can now access these parameters by name
 mov   qword [lParam], R9

 cmp   qword [uMsg], WM_DESTROY                 ; [RBP + 24]
 je    _WMDESTROY

_DefaultMessage:
 ; DefWindowProcA需要4个参数,4*8=32 32/16=2 能对齐不用手动对齐
 sub   RSP, 32                                  ; 32 bytes of shadow space
 mov   RCX, qword [hWnd]                        ; [RBP + 16]
 mov   RDX, qword [uMsg]                        ; [RBP + 24]
 mov   R8, qword [wParam]                       ; [RBP + 32]
 mov   R9, qword [lParam]                       ; [RBP + 40]
 call  DefWindowProcA
 add   RSP, 32                                  ; Remove the 32 bytes

 mov   RSP, RBP                                 ; Remove the stack frame
 pop   RBP
 ret

_WMDESTROY:
 sub   RSP, 32                                  ; 32 bytes of shadow space
 xor   ECX, ECX
 call  PostQuitMessage
 add   RSP, 32                                  ; Remove the 32 bytes

 xor   EAX, EAX                                 ; WM_DESTROY has been processed, return 0
 mov   RSP, RBP                                 ; Remove the stack frame
 pop   RBP
 ret

x86 基本窗口扩展 看原文

对基本窗口示例的改进:

  • 窗口将在屏幕上居中
  • 客户区现在将是正确的大小(在调整大小之前)
  • 窗口具有背景色
  • 将创建2个静态控件。单击时文本会更改颜色
  • 创建2个编辑控件。Tab键可用于更改焦点
  • 静态和编辑控件的字体从系统默认值更改
  • 窗口上有一个黑色矩形
  • 关闭窗口时显示退出确认

x64 基本窗口扩展 看原文

对基本窗口示例的改进:

  • 窗口将在屏幕上居中
  • 客户区现在将是正确的大小(在调整大小之前)
  • 窗口具有背景色
  • 将创建2个静态控件。单击时文本会更改颜色
  • 创建2个编辑控件。Tab键可用于更改焦点
  • 静态和编辑控件的字体从系统默认值更改
  • 窗口上有一个黑色矩形
  • 关闭窗口时显示退出确认
posted @ 2020-07-01 16:42  Ajanuw  阅读(3599)  评论(0编辑  收藏  举报