windows学习

本文详细介绍了Windows消息机制的工作流程,包括事件转化为消息、消息的接收与处理等环节,并探讨了进程的概念、进程切换的过程及资源分配方式。此外,还讲解了线程同步的方法、进程间通信手段以及动态链接库的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

windows消息机制:
1) 系统中发生了某个事件。
2) Windows把这个事件翻译为消息,然后把它放到消息队列中。
3)应用程序消息队列中接收到这个消息,把它存放在TMsg记录中。
4)应用程序把消息传递给一个适当的窗口的窗口过程。
5) 窗口过程响应这个消息并进行处理。
Windows消息控制中心一般是三层结构,其顶端就是Windows内核。Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control)。第二级控制中心一般是各Windows应用程序的Application对象。第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息。

进程:
进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器
这里所说的从某个进程收回处理器,实质上就是把进程存放在处理器的寄存器中的中间数据找个地方存起来,从而把处理器的寄存器腾出来让其他进程使用。那么被中止运行进程的中间数据存在何处好呢?当然这个地方应该是进程的私有堆栈
让进程来占用处理器,实质上是把某个进程存放在私有堆栈寄存器的数据(前一次本进程被中止时的中间数据)再恢复到处理器的寄存器中去,并把待运行进程的断点送入处理器的程序指针PC,于是待运行进程就开始被处理器运行了,也就是这个进程已经占有处理器的使用权了。
在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的 切换实质上就是被中止运行进程与待运行进程上下文的切换。在进程未占用处理器时,进程 的上下文是存储在进程的私有堆栈中的。

相关知识:
程序可以作为一种软件资料长期存在,而进程是有一定生命期的。程序是永久的,进程是暂时的。
通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。
另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。

线程同步:
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。


进程间通信:
(1) 管道分为有名管道和无名管道,无名管道只能用于亲属进程之间的通信,而有名管道则可用于无亲属关系的进程之间。
(2) 消息队列
(3) 信号量
   (4) 共享内存
      (5) 网络Socket

动态链接库:

动态链接库缩写为DLL,在电脑中可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。
当前木马病毒经常感染或者替换系统文件dll文件。
导出:
方法一、向导出的 DLL 函数中添加函数关键字
要使用函数 关键字,您必须使用以下 关键字来声明要导出的各个函数:
__declspec( dllexport)
要在 应用程序中使用导出的 DLL 函数,您必须使用以下关键字来声明要导入的各个函数:
__declspec( dllimport)
通常 情况下,您最好使用一个包含 define 语句和 ifdef 语句的头文件,以便分隔导出语句和导入语句。
方法二、创建 模块定义文件(.def) 以列出导出的 DLL 函数


在程序编译的时候,头文件是不参与编译的,源文件预编译的时候,如果碰到#include <xxx.h>,就把xxx.h中的文本内容全部复制到相应的位置,在编译Dll1.cpp文件时,首先定义DLL1_API宏,将其定义为_declspec(dllexport),然后碰到Dll1.h头文件,将其展开,由于已经定义了Dll1_API,所以后面的两个函数的声明都是_declspec(dllexport),表示为导出函数,之后这个Dll交给其他程序使用,先包含头文件,只要这个函数没有定义DLL1_API,则对于其他程序,这两个函数都是_declspec(dllimport),表示导入函数。


#define DLL_API extern "c" _declspec(dllexport)
这样编译器就不会进行名字改编,导出的函数跟我们自己写的一模一样,
1.因为调用LoadLibrary时动态加载动态链接库,所以不需要头文件和.lib文件
2.如果我们在动态链接库中使用标准调用约定_stdcall来导出函数,在模块定义文件方式下,导出的函数名字不会发生改编。但是在客户端的可执行程序中,定义函数指针类型时也要采用_stdcall调用约定:typedef int(_stdcall * ADDPROC)(int a , int b );
3.动态调用动态链接库时,最好不要发生名字改编,不然函数名字发生改编之后我们不知道函数名字就无法根据函数的名字进行调用了。当然如果知道DLL中函数的序号,这时可以使用宏MAKEINTRESOURCE把序号转变成名字,如:
ADDPROC Add=(ADDPROC)GetProcAddress( hInst,MAKEINTRESOURCE (1));

//加载动态连接库头文件
#include"../dll/test.h"
//加载动态连接库的引入库(LIB)
#pragma comment(lib, "../release/dll.lib")

两种加载方式的比较:

动态加载和隐式链接这两种加载DLL的方式各有优点.如果采用动态加载的方式,那么可以在需要加载时才加载DLL.而隐式链接方式实现起来比较简单,在编写客户端代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的函数,但是访问十多个DLL,如果都采用隐式链接的方式链接加载他们的话,那么在启动程序时候,这些DLL都需要加载到内存中,并映射到调用进程的地址空间,这样将加大启动程序的时间,而且,一般来说,在程序运行过程中只是在某个条件满足时候,这时才会访问某个DLL中的某个函数,其他情况下都不需要访问这些DLL,但是,这时所有的DLL都被已经加载到内存中,资源浪费会比较严重,这种情况下,就可以采用动态加载DLL技术,也就是说,在需要时,DLL才会被加载到内存中,并被映射到进程的地址空间中,有一点需要说明的是,实际上,采用隐式链接的方式访问DLL时,在启动时也是通过调用LoadLibrary函数加载到该进程需要的动态链接库的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MyObject-C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值