记录一些学习WIN32知识的心得
Win32消息
Windows中的每一个动作都是一个 事件
而为了记录这些时间 产生了消息这么一个结构
消息结构
/*
* Message structure
*/
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
hwnd
窗口句柄 用于标识消息所属的窗口
message
消息编号 表示消息的类型
wParam
消息参数 用于进一步解释消息
lParam
消息参数 用于进一步解释消息
time
触发时间
pt
类型为POINT类 结构如下
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
用于记录消息位置
消息机制
系统队列
系统会维护一个队列 消息在里面排队
这个系统队列会对消息进行分流
应用队列
将消息通过窗口句柄发给对应的应用程序
应用程序也会有一个消息队列
消息循环
在这个队列里应用程序会通过循环对每个消息类型进行筛选 对关心的类型执行自己的处理函数
如果是不关心的消息类型 就交给系统处理
Win32窗口
窗口结构
typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;
包含窗口的各种属性
lpfnWndProc
重要 窗口消息处理函数
hInstance
lpszClassName
必要 窗口名字
创建自定义窗口步骤
- 创建好窗口的结构体
- 通过
RegisterClass
对窗口进行注册(通知Windows) - 在需要调用
CreateWindow
创建窗口
Win32IPC对象
对象概括
已经学到的有进程、线程、互斥体、事件、信号量
内核对象都有已通知和未通知两种状态
比如说创建事件时第二个参数是是否开启手动模式(FALSE自动将已通知转换为未通知) 第三个参数是初始状态
想要同时读取就开手动模式(让大家都能读) 想要互斥写就需要开启自动复位(一个开始写另一个需要等待)
内核对象都可以跨进程
比如说对互斥的控制 临界区只能在单个进程内使用 而互斥体可以跨进程
但缺点是 内核对象性能较差 (因为需要进入到0环)
线程 Thread
线程可以说是进程要跑起来的基础 , 这里可以详细讲一下
创建线程
函数体如下
//创建进程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//安全属性
SIZE_T dwStackSize, //分配栈大小
LPTHREAD_START_ROUTINE lpStartAddress, //线程处理函数
__drv_aliasesMem LPVOID lpParameter, //传递给线程处理函数的参数
DWORD dwCreationFlags, //A pointer to a variable to be passed to the thread.
LPDWORD lpThreadId //The flags that control the creation of the thread.
);
我们可以看到, 为了成功创建线程, 我们通常需要先准备好一个线程处理函数, 这个线程函数需要满足一定的格式
继续往下看
线程处理函数
类似于消息框也需要一个消息框处理函数, 创建一个线程也需要一个线程处理函数
函数体形式应当如下
DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) {...}
互斥体 Mutex
基本就是临界区(Critical Section)一样的功能, 区别就是一个是内核对象, 另一个是普通进程内的对象
//创建互斥体
HANDLE WINAPI CreateMutex(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner, //初始状态
_In_opt_ LPCTSTR lpName
);
//进入互斥体
HANDLE WINAPI OpenMutex(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCTSTR lpName
);
//释放互斥体
BOOL WINAPI ReleaseMutex(
_In_ HANDLE hMutex
);
事件 Event
//创建事件
HANDLE WINAPI CreateEvent(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset, //是否开启手冲模式
_In_ BOOL bInitialState, //设置初始状态
_In_opt_ LPCTSTR lpName
);
//例子
f_Consumed = CreateEvent(NULL, TRUE, TRUE, NULL);
//设置事件状态为TRUE
SetEvent(f_Consumed);
//关闭句柄
CloseHandle(f_Consumed);
Q: 什么时候会用到手冲模式呢?
A: 一般情况不会用到, 除非想让大家任意访问, 但这样存在的意义也不大了, 所以一般都是关闭手冲模式的, 来实现互斥用的
信号量 Semaphore
//创建
HANDLE g_Semaphore = HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount, //初始发放资源数目
LONG lMaximumCount, //最大并行资源数目
LPCTSTR lpName
);
//申请一个空闲区域, 信号量第二个参数-1
WaitForSingleObject(g_Semaphore, INFINITE);
//释放,第二个参数表示释放后空闲的资源数目
ReleaseSemaphore(g_Semaphore, 1, NULL)
线程Context
线程之间互相切换的时候状态
这个结构就有了保存其线程寄存器状态的作用
使用方法
- 先定义CONTEXT结构 依据想要获取的值对成员
ContextFlags
进行赋值 - 调用GetThreadContext, 就可以在定义的CONTEXT结构看到想要的寄存信息了
进程就是4GB 线程就是EIP
跨进程的互斥和同步
互斥就是同一时刻不同访问
同步就是在一个执行之前需要保证另外一个优先执行 也就是要达到按序访问的目的
在学到的内核对象中 做一些小总结
实现互斥的工具
单进程可以有临界区 或者自己实现
这里主要还是讨论跨进程的内核对象
互斥体, 事件, 信号量都可以作互斥
- 互斥体: 作用和最基本的临界区一致
- 事件: 在临界区功能的基础上, 能够实现简单的进程同步(例如利用两个事件来解决生产者-消费者问题)
- 信号量: 在可以实现临界区的基础上, 信号量更加灵活, 例如可以同时让有限个线程进入临界区
实现同步的工具
- 事件: 在临界区功能的基础上, 能够实现简单的进程同步(例如利用两个事件来解决生产者-消费者问题)
- 学习中
近期重新整理