开始了解窗口

1.窗口是什么

1.)使用窗口的原因

  • 懒得说了,自行查阅

2.)窗口和程序的关系

  • 一个窗口不一定是程序。可能是一个程序的一部分
  • 一个程序也不能是一个窗口
  • 窗口是人和计算机交互的界面
  • 第一个标准的窗口为界面的程序的架构,而不是windows所有程序

2.窗口界面

  • 窗口大部分都长得差不多
1
类的继承
  • 窗口中有很多的部分,每一个部分都有自己的行为模式。
1
2
类-->对象
就是使用api函数来写程序

3.窗口程序是如何工作的

1.)窗口程序的运行模式

1
面向对象	面向过程
  • 窗口程序是事件驱动的
1
类似:面向对象

2.)FirstWindow源代码

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
		.386 
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?

hInstance dd ?
hWinMain dd ?

.const

szClassName db 'MyClass',0
szCaptionMain db 'My first Window !',0
szText db 'Win32 Assembly, Simple and powerful !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
local @stPs:PAINTSTRUCT
local @stRect:RECT
local @hDc

mov eax,uMsg
;********************************************************************
.if eax == WM_PAINT
invoke BeginPaint,hWnd,addr @stPs
mov @hDc,eax

invoke GetClientRect,hWnd,addr @stRect
invoke DrawText,@hDc,addr szText,-1,\
addr @stRect,\
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd,addr @stPs
;********************************************************************
.elseif eax == WM_CLOSE
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
;********************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
;********************************************************************
xor eax,eax
ret

_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG

invoke GetModuleHandle,NULL
mov hInstance,eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 注册窗口类
;*******************************************************************
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
offset szClassName,offset szCaptionMain,\
WS_OVERLAPPEDWINDOW,\
100,100,600,400,\
NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循环
;********************************************************************
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

end start

  • 在windows下的编程,基本都是调用API函数

3.)框架分析

  • 读代码首先要找入口
1
start -> _WinMain -> Exitprocess
  • 从调用的API函数入手
1
GetModuleHandle-->RtlZeroMemory-->LoadCursor-->RegisterClassEx-->CreateWindowEx-->ShowWindow-->UpdateWindow
  • 然后是三个API函数的循环
1
2
3
4
5
6
.while 	TRUE 
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
1
GetMessage -->TranslateMessage -->DispatchMessage

image-20230423230508501

  • 回调函数,就是windows自己调用的函数,不是我们自己调用的

  • windows在系统内部有一个系统消息队列,当输入设备有所动作的时候,windows都会记录在队列里面

4.)PostMessage和SendMessage

  • PostMessage是吧消息放到其他程序的消息队列中
1
如:图中的d箭头,目标程序收到了这条消息就把他放入该程序的消息队列处理
  • SendMessage则越过直接调用目标程序的窗口
1
如:途中的箭头I,窗口过程返回以后才从SendMessage返回,如箭头II

分析窗口程序

  • 下边将详细分析窗口程序咯
  • 调用API函数,来实现功能。

1.模块和句柄

1.)句柄是什么

  • 句柄是windows用来表示各种资源的编号
  • windows中的东西都是用句柄来标识的,直接用就好了

2.)模块是什么

  • 一个模块代表的是一个运行中的exe文件或dll文件,用来代表这个文件中所有的代码和资源,磁盘上的文件不是模块,装入内存后运行的时候叫模块
  • 取模块句柄使用的API函数是 GetModuleHandle
1
invoke GetModuleHandle,lpModuleName
  • lpModuleName 参数是指向该模块名称 字符串的指针

  • 比如想得到User32.dll的句柄

1
2
3
4
5
6
7
szUserDll	db	'User32.dll',0
...
invoke GetModuleHandle,addr szUserDll
.if eax
mov hUserDllHandle,eax
.endif
...
  • 如果参数是NULL则GetModuleHandle,那么得到的是调用本模块的句柄
1
2
invoke	GetModuleHandle,NULL
mov hInstance,eax
  • h的意思就是句柄

2.创建窗口

  • 类【不多说】
  • windows中创建窗口使用这样的层次结构
1
1.定义一个床亏类

1.)注册窗口类

  • 注册窗口类的API函数是 RegisterClassEx
1
Ex是扩展的意思
1
2
3
local	@stWndClass:WNDCLASSEX
...[通过索引这个对象中的东西去修改]
invoke RegisterClassEx,addr @stWndClass
  • 这个函数里面会有很多属性,这些属性应该用多个参数送过去,但是太多了,所以就吧这些参数定义在一个类中WNDCLASSEX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
WNDCLASSEX STRUCT
cbSize DWORD ? ;结构的字节数
style DWORD ? ;类风格
lpfnWndProc DWORD ? ;窗口过程的地址
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ? ;所属的实例句柄
hIcon DWORD ? ;窗口句柄
hCursor DWORD ? ;窗口光标
hbrBackground DWORD ? ;背景色
lpszMenuName DWORD ? ;窗口菜单
lpszClassName DWORD ? ;类名字符串的地址
hIconSm DWORD ? ;小图标
WNDCLASSEX ENDS
  • 程序定义了一个WNDCLASSEX结构的变量@stWndClass,用RtlZeroMemory API函数将他里面的内容全部填写0【这里是局部变量,所以需要置为0】,再填写结构的各个字段。

  • 这里每一个参数都有自己的意义和用法,强烈建议查资料,或者看书P101-102

  • 补充一个知识点,对于不同二进制位组合的计算。强烈建议使用or

1
2
3
4
5
当某个东西是靠着,二进制某些位上的0/1来判断是否使用的某功能的时候,如果好几张功能想同时使用,一般下是使用or【逻辑或】来进行。
比如:P102下面的小灯泡。

or ==> 一真即真 【不懂自己百度】
and ==> 一假即假

2.)建立窗口

  • 在注册窗口类的时候,是把窗口很多“共性”都写在了一起。
  • 建立窗口,就还需要去指定窗口的很多“个性化”的参数
  • 建立窗口的API函数是:CreateWindowEx
1
invoke  CreateWindowEx,dwExStyle,lpClassName,lpWindowName,dwStyle,\          x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam
  • 它每一个参数都有自己的意义
1
请结合书P103-104
  • 把窗口显示出来的API函数是:ShowWindow
1
2
ShowWindow,句柄,显示方式
第一个参数是句柄,第二个是显示方式P106

3.)整一个按钮

  • 这个去看windowpro文件

3.消息循环

  • windows是一个以消息驱动的操作系统
  • windows中有一个消息队列
  • 应用程序中含有一段称为“消息循环”的代码,用来从消息队列中检索这些消息并把他们分发到相应的窗口函数中

1.)消息循环的一般形式

1
2
3
4
5
6
7
8
local 		@stMsg:MSG

.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
  • 消息循环中要用到一个MSG结构
1
2
3
4
5
6
7
8
MSG	STRUCT
hwnd DWORD ?
message DWORD ?
wparam DWORD ?
lparam DWORD ?
time DWORD ?
pt POINT <>
MSG ENDS
  • 其中的含义请看书P107
  • 这个结构定义了消息的所有属性,GetMessage就是从消息队列中取出这样一条消息的
1
invoke	GetMessage,lpMsg,hWnd,wMsgFilterMin,wMsgFilterMax
  • hWnd参数指定要获得哪个窗口的消息,例子中为NULL则就是本程序所属窗口的消息

  • TranslateMessage

1
TranslateMessage,addr @stMsg 
  • DispatchMessage
1
DispatchMessage,addr @stMsg 
  • 作用请看P108【确实很烦,多看几遍】

2.)其他形式的消息循环

  • 首先我们得知道的是:任务之间是20ms切换一次,在到某个任务的时候,不论在工作或者等待,都得等待20ms才会进入下一个任务
1
所以如果GetMessage中没有任何消息的话,就会等待20ms,从而浪费时间。【这个浪费时间其实是对CPU的内耗,因为CPU要一直从消息队列里面去获取消息,但是又获取不到消息,这样就会白白的消耗CPU】
  • GetMessage函数是程序空闲的时候主动将控制权 windows的一种方式,windows是一个抢占式的多任务系统…
  • 这里如果啥都没有就会时间内耗,在while循环中等待,没任何意义,所以有更好的办法。
    • 用PeekMessage api函数
1
2
3
4
5
6
7
8
9
10
.while	TRUE
invoke PeekMessage,addr @stMsg,NULL,0,0,PM_REMOVE
.if eax
.break .if @stMsg.message == WM_QUIT
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.else
<其他的工作>
.endif
.endw
  • PeekMessage是一个类似GetMessage的函数,区别在于当消息队列里面有消息的时候,PeekMessage取回消息,并在eax中返回非零值。
  • 当消息队列里面没有消息的时候,就可以直接去运行其他工作,达到很好的使用。
  • PeekMessage前面的参数和GetMessage一样。
  • 最后一个参数表示在取回消息后,对消息队列中的消息是否保留。这里使用PM_REMOVE就是不保留。

4.窗口过程

  • 首先窗口过程,是给windows回调用的。【也就是说它是一个函数】并且是一个自己创建的函数【这个函数的参数是定好的了的】

  • 这个窗口过程,对于windows来说,地址来才是所需要的。

1
WindowProc	proc	hwnd, uMsg, wParam, lParam
  • 第一个参数是窗口句柄,由于一个窗口过程可能为多个基于同一个窗口类的窗口服务,所以windows回调的时候必须指定要操作的,要对这个对象进行运用都需要句柄

  • 第二个参数是消息标志,后面俩个参数是消息的俩个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WindowProc	proc	uses ebx edi esi hWnd,uMsg,wParam,lParam
mov eax,uMsg
.if eax == WM_XXX
<处理WM_XXX消息>
.elseif eax == WM_YYY
<处理WM_YYY消息>
.elseif eax == WM_CLOSE
invoke Desinvoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif

xor eax,eax
ret
WindowProc endp
  • 这个过程主要是对uMsg参数中的消息编号构成一个分支语句

  • 但对于需要处理的消息分别处理。不感兴趣的消息都给DefWindowProc处理

  • ebx edi esi ebp 是指针寄存器,需要保存起来

  • ecx edx 就不需要咯

  • 又很多内容,请看P110

2.)收到消息的顺序

P111-112

3.)消息默认处理–DefWindowProc

窗口间的通信

1.窗口间的消息互发

Emmmm 学了这么多,总感觉迷迷糊糊的。要不直接来学学二进制吧。哈哈啊哈