cyxyrq-code-loading

 

Win32学习5

17、消息类型

①消息的产生与处理流程:

image-20230211225017698

其实窗口创建的流程比它的代码层面更加的重要。理清楚上图的消息流程。

操作系统会将不同的操作产生的不同的消息,将所有的消息都分一个类别,设置唯一的编号。

typedef struct tagMSG{
HWND hwnd; //消息对应的某个窗口
UINT message; //消息所对应的编号
WPARAM wParam; //描述消息是怎么样的
LPARAM lParam; //描述消息是怎么样的
DWORD time; //消息所生成的时间
POINT pt; //消息所产生的位置
#ifdef _MAG
DWORD lPrivate;
#endif
}MSG,*PMSG, NEAR *NPSMG , FAR *LPMSG;

message 每个操作都有不同的信息,所以我们可以通过打印这个结构体中的 message成员来获取当前这个信息对应的是什么操作。

消息时刻都是在产生的,我们只需要操作我们感兴趣的消息。一般我们利用 switch 这个判断语句来捕捉对应的操作,来分别不同的情况,在case 中添加自己跌业务逻辑。例如如下:(关闭窗口的时候关闭进程)

switch(Msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}

wParam 和 lParam 这两个参数在不同的消息类型中具有不同的意义,可以通过查询文档来具体查看是起到了什么作用。而在消息中多是虚拟码 virtual code,所以我们需要TranslateMessage这个函数将虚拟码转化成字符,操作系统才能更好的识别。

18、子窗口控件

子窗口控件: <1> WINDOWS提供了几个预定义的窗口类以方便我们的使用,我们一般就它们 叫做子窗口控件,简称控件。 <2>控件会自己处理消息,并在自己状态发生改变时通知父窗口。 <3> 预定义的控件有: 按钮、复选框、编辑框、静态字符串标签和滚动条等

//我们在窗口创建的时候,创建文本框
CreateWindow(
"EDIT",
   "",
   WS_CHILD|WS_VISIBLE|WS_VSCROLL,//子窗口,立即可见,有滚动条
   0
   0
   70
   50
   hwnd,//父窗口的句柄
   NULL,//子窗口的编号(用于区分同一类控件中的不同子控件,其值可以随意)
   Instance,
NULL
)

子窗口控件与我们创建的父窗口没有本质上的区别,主要是因为windows为我们提供好了子窗口,能够对于消息做出反应,我们只需要通过当其状态改变对于父窗口的通知做出响应。我们可以通过将其传出的消息进行打印,进而可以得到对应的message,因而就可以对于对应的消息,进行我们想要执行的操作。

19、虚拟内存与物理内存

每个进程都有自己 4 GB 的内存空间,从0 开始到FFFFFFFF结束,但是实际上并非每个都是实际的内存,我们可以理解为这个 4 GB的内存空间属于是 windows给进程开的空头支票。这就是虚拟内存的概念。虽然内存是虚拟的,但是数据并非虚拟的,是真实存在的,所以该数据是储存在物理内存上的。只有真正需要使用内存的,才是储存在物理内存上的,而那些不需要物理内存的,就仅仅是一张“空头支票”。

X86这种 cpu 是由将内存分为 4 kb一页一页的(页式管理),但并非所有的架构都是4kb,ARM架构的就是2kb

①虚拟内存与物理内存的关系

image-20230215233025883

虽然这个物理页就可以理解为是物理内存,但是实际上这个物理页和真正的物理内存,即内存条,还有一层映射。

②虚拟内存地址划分

image-20230215233717902

每个进程真正的虚拟内存只有 2GB,因为高2GB的内存属于内核,用户无法进行操作。而低2GB用户能够使用的还需要排除空指针赋值区以及 64 KB 禁入区。

虽然这些可供使用的内存都属于一个进程,但是我们用户实际上很有可能用不完这些,并没有完全分配,可能只用到了很小的一部分。当我们需要申请内存的时候,用户空间才会有对应的内存,才会有实际的物理页。

物理内存的大小实际上就是内存条的大小,而我们通过计算就可以得到对应的物理页的数量有多少。

特别说明: <1>线性地址有 4G 但未必都能访问 <2>所以需要记录,哪些地方分配了 <3>操作系统中的虚拟内存实际上是操作系统为了避免内存不够使用,将硬盘划分一部分作为虚拟内存(pagefile.sys),需要区别此处的虚拟内存。

③物理内存:

可供使用的物理内存: MmNumberOfPhysicalPages * 4 = 物理内存 虚拟内存(硬盘) 能够识别的物理内存: 32位系统最多可以识别物理内存为64G,但由于操作系统的限制 比如XP,只能识别4G(Windows 2003服务器版本可以识别4G以上)

④七个坛子八个盖:

image-20230708151606364

一个进程不是一直都拥有同一个物理页的,因为操作系统会进行判断,在足够的情况下,将物理页给更加急需要的进程使用,因为毕竟有一些不需要真正的内存,只有已经分配的虚拟内存才能拥有物理页,才能在物理页中有存取数据。

20、私有内存的申请释放

我们可以理解我们所使用的、所申请的内存可以分为两种,一种称为私有的(Private),另一种称为映射的(Mapped)。

如果一个物理页,只被一个进程所使用,那么称当前这个物理页是属于私有的;但是如果这个物理页,被多个进程所使用的,那么称当前这个物理页是映射的(Mapped)

①申请内存的两种方式: <1>通过VirtualAlloc/VirtualAllocEx 申请的: Private Memory <2>通过CreateFileMapping 映射的: Mapped Memory

VirtualAlloc是在当前的进程申请内存,VirtualAllocEx 而可以指定一个进程 ID 即可以在别的进程中申请内存。

②内存申请与释放:

LPVOID VirtualAlloc{
LPVOID IpAddress, // 要分配的内存区域的地址
DWORD dwSize, //分配的大小
DWORD flAllocationType, //分配的类型
DWORD fiProtect // 该内存的初始保护属性
};

IpAddress这个成员一般没有特殊需求我们不做设置,因为如果指定到已经被占用的内存区域的话,内存一定会是申请失败的。 dwSize 这个成员是分配的大小,一般来说我们都是填写的页的整数倍。 flAllocationType 这个成员是分配的类型,一般来说我们填写的是两种类型,一种是MEM_RESERVE 以及 MEM_COMMIT ,前者是占据一段内存区域但暂不需要物理页,后者不仅是占据了内存区域而且还需要使用物理页。 fiProtect 这个成员代表了属性,属性大致有可读、可写、可执行代码等等。

申请内存之后,对应的就是释放内存。

BOOL VirtualFree(
LPVOID lpAddress, // address of region 内存地址
SIZE_T dwSize, // size of region 大小
DWORD dwFreeType // operation type 类型
);

VirtualFree(p,0x1000,MEM_DECOMMIT) 如果是这样的形式,就是释放掉对应的物理页,但仍旧保留对应的虚拟内存区域。 VirtualFree(p,0,MEM_RELEASE) 用MEM_RELEASE 这个类型的时候,大小必须填0,这样的话,不仅释放掉物理页,还会释放掉对应的虚拟内存区域。

③堆与栈

int main(int argc, char* argv[])
{
int x= 0x12345678; //栈内存
int* y= (int*)malloc(sizeof(int)*128);//堆内存
   
   //new = malloc +构造函数
printf("栈:%x \n",&x);
printf("堆: %x \n",y);
getchar();
return 0;
}

malloc 的申请内存实际上是在已经申请的内存上,取一部分占为己用。

posted on 2023-07-17 07:57  清雨中欣喜  阅读(13)  评论(0编辑  收藏  举报

导航