图解win32应用程序框架

本文详细介绍了如何使用Win32 API创建窗口程序,包括注册窗口类、创建窗口、消息循环及窗口过程函数等内容,并通过比喻的方式帮助理解。

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

如果把操作系统比喻成一个生产汽车的公司;应用程序在内存的那块空间比喻成为一个汽车工厂的厂房;窗口类WNDCLASS比喻成为图纸(或生产汽车用到的部件的清单);窗口Window比喻成为具体的汽车(如奥迪A6)。则整个生产汽车的过程(建立窗体的过程)如下图:

需要注意的难点是:如果注册多个窗口类并且每个窗口类都对应一个自己的窗口函数,则相当于注册了多个窗口函数(全局的函数)到操作系统,那么while消息循环中DispatchMessage(&msg); 这一句话会根据hWnd参数即窗口句柄把对应的msg发送都对应的窗口函数(至于怎么对应的,应该是hWnd找到那个窗口对象,而每个窗口对象又都是根据某个注册的窗口类来创建的,那么每个窗口对象里面应该保存有对应窗口类的名称,也就是上面图中清单的名称,所以更具hWnd就能找到那个窗口类即清单,找到了清单自然就能找到对应的窗口函数了),所以hWnd参数是很重要的。关于这个问题我已经测试过了。

总之:可以简单的认为hWnd参数和某个窗口函数对应或关联,hWnd确定了,则DispatchMessage函数就能够转向某个窗口函数了。

 

下面解释新建一个空的Win32应用程序书写代码的步骤:

首先,exe应用程序由操作系统调用进入内存,再给应用程序分配好内存后,操作系统给这个应用程序一个HINSTANCE句柄,然后操作系统调用应用程序的winmain函数开始执行。

winmain函数声明如下:

#include <windows.h>    //windows用到的相关头文件,编写win32程序必须包含这个头文件

#include<stdio.h>  //C语言的库函数所在的头文件,可能用到C语言的库函数,所以要包含进去

int WINAPI WinMain(HINSTANCE hInstance,      
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)

这个winmain函数是由操作系统调用的,所以里面的参数也都是由操作系统赋值的。其中hInstance指标志这个应用程序的句柄;hPrevInstance标志应用程序前一个应用程序的句柄,在win32下一般规定就是0;lpCmdLine是用户执行应用程序时传的命令行参数,比如dos下执行1.txt,则这个1.txt就是命令行参数,根据这个参数打开或新建对应的文件;nCmdShow指程序运行的主窗口显示的状态,是最大化、最小化还是隐藏显示。

 

第二,注册窗口类和创建具体的窗口

如果把产生一个窗体的过程比喻成生产一辆汽车,则生产这辆汽车需要地盘、轮子等部件,还可能需要汽车工程师等人员来装配。而窗口类WNDCLASS就是产生这辆汽车需要所有物件的清单,它描述了需要哪些部件才能生产出这辆汽车(也就是产生这个窗口)。在这个清单中,最重要的部件就是窗口函数的指针和应用程序实例句柄。

(1)窗口类的定制和注册,就是把清单中需要的具体部件都拿过来(或都产生出来,包括图标、光标、画刷等资源,这些部件可以被共享的,节约了内存)。

下面是把注册窗口类的所有过程封装在了一个函数MyRegisterClass中:

void MyRegisterClass(HINSTANCE hInstance) {  

     WNDCLASS wc;  //定义一个窗口类的实例,即一个空清单

     wc.style   = CS_HREDRAW | CS_VREDRAW;  //窗口类的样式,都是全局的宏定义,定义在WinUser.h中,这个样式是指较全局话的样式,比如是豪华车型还是普通车型,而不是指具体的车型如奥迪A6

     wc.lpfnWndProc = WndProc; //窗口函数的首地址记录下来

     wc.cbClsExtra  = 0;  //给这个窗口类的实例分配多大的扩展或保留内存,一般为0

     wc.cbWndExtra  = 0;  //给以后生成的每个具体窗口的实例分配多大的扩展或保留内存,一般为0

     wc.hInstance  = hInstance;  //本窗口对应的应用程序的句柄是多少

     wc.hIcon   = LoadIcon(NULL/*或hInstance*/, IDI_APPLICATION);  //图标资源,其中hInstance是winmain中传来的句柄,为null时表示使用默认的标志图标,IDI_APPLICATION是全局的宏定义

     wc.hCursor  = LoadCursor(NULL, IDC_ARROW);  //同上

     wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);  //背景画刷,DKGRAY_BRUSH是全局宏定义

     wc.lpszMenuName = NULL;  //菜单名,NULL表示不用菜单

     wc.lpszClassName = "某个窗口类"; //这个窗口类实例的名字,标记这个窗口类的实例  

     RegisterClass(&wc); //窗口类实例的注册

}

窗口类实例的注册是在一个专门的内存区域进行备案,以后随时根据窗口类实例的名字挑选出来使用,用如下图可以便于理解:

(2)窗体的创建

HWND CreateWindow(

  LPCTSTR lpClassName,  //安装清单的名称,即窗口类实例的名称

  LPCTSTR lpWindowName,  //窗口实例的标题

  DWORD dwStyle,  //窗口实例的样式,具体的样式,具体的车型,比如奥迪A6

  int x,  //窗口左上角坐标值x

  int y,  //窗口左上角坐标值y

  int nWidth,  //窗口的宽度

  int nHeight,  //窗口的高度

  HWND hWndParent,  //父窗口的句柄

  HMENU hMenu,  //主菜单的句柄

  HANDLE hInstance,  //应用程序实例句柄,和窗口类实例一样都有

  LPVOID lpParam //WM_Create消息用到的附加参数,一般只在mfc中用

);

(3)窗口的显示

bool ShowWindow(

    HWND hwnd, //窗口实例的句柄

    int nCmdShow  //窗口的显示方式,最大化等

);

bool UpdateWindow(

    HWND hWnd  //窗口实例的句柄

);

 

第三、消息循环

消息循环是属于整个应用程序的(所以放在main函数中),因为消息循环要从消息队列中取消息,而消息队列是属于整个应用程序的。

消息结构的定义如下:

typedef struct tagMSG{

  HWND hwnd;  //产生消息的那个窗口实例的句柄

  UINT message;  //消息的类型(即事件的类型)

  WPARAM wParam;  //消息的附加信息1,比如存放WM_CHAR消息时的对应按键的ASCII码

  LPARAM lParam;  //消息的附加信息2,具体功能还不知道

  DWORD time;  //消息被投递到应用程序队列的时间

  POINT pt;  //当消息被投递时,光标在屏幕上的位置

}MSG;

消息循环的代码如下,在main函数中:

while(GetMessage(&,NULL,NULL,Null)){ //GetMessage函数只有收到WM_QUIT消息时才返回false

  TranslateMessage(&msg);  //对消息进行转换,如解释键盘消息,即当键盘按下时是WM_KeyDown消息,这个函数转换产生一个WM_CHAR消息并放入到应用程序消息队列中

  DispatchMessage(&msg);  //把消息路由给(发送给)操作系统,最后由操作系统负责调用具体的消息处理函数(窗口函数)

  //我猜测分发消息时,是根据msg中的产生消息的那个窗口实例的句柄找到对应的窗口类的实例(清单),再根据这个窗口类实例(清单)找到具体的窗口函数

}

下面讲讲GetMessage函数

bool GetMessage(

  LPMSG lpMsg,  //装具体的消息

  HWND hWnd,  //窗口句柄,指示我要获取那个窗口的消息并装入到第一个参数lpMsg中,为NULL表示我要获取所有窗口的消息

  UINT wMsgFilterMin,  //消息范围的最小值,表示我要获取比这个值大的消息然后装入lpMsg中

  Uint wMsgFilterMax  //消息范围的最大值,如果最小和最大范围都设为0则表示不设置范围,获取所有范围的消息。

);

 第四、窗口过程(函数)

LRESULT CALLBACK WndProc_XXX(  //LRESULT是个LONG型,CALLBACK是_stdcall调用约定。 下面的四个参数其实就是消息结构体MSG的前四个参数(去掉了时间和光标当前位置)

  HWND hwnd,   //窗口句柄,由操作系统赋值

  UINT uMsg,  //消息类型,由操作系统赋值

  WPARAM wParam,  //消息附加参数,由操作系统赋值

  LPARAM lParam  //同上

)

{

  HDC hdc;

  PAINTSTRUCT ps;

  switch (uMsg)

  {

    case WM_PAINT:  //窗体重绘消息。如果在这里设置一个断点,则由于窗口从无到有都执行到这里,所以窗口就永无法显示出来

      hdc=BeginPaint(hwnd,&ps);  //所有的DC都是和窗口相关的

      TextOut(hdc,0,0,"初始显示的文字",strlen("初始显示的文字"));

      EndPaint(hwnd,&ps); //和BeginPaint是一对。另外BeginPaint函数只能用在WM_PAINT窗口重绘消息中,不能用在其他消息中

      break;

    case WM_CHAR:

      char szChar[20];

      spintf(szChar,"char is %d",wParam);  //格式化字符串到内存szChar中

      MessageBox(hwnd,szChar,"标题",MB_OK);

      break;

    case WM_LBUTTONDOWN:

      MessageBox(hwnd,"您按下了鼠标左键","标题",MB_OK);

      HDC hDC;  //看作是一个和具体窗口关联了的画布

      hDC=GetDC(hwnd);

      TextOut(hDC,0,50,"画出了一些文字",strlen("画出了一些文字"));  //在画布hDC上画文字

      ReleaseDC(hwnd,hDC); //和GetDC是一对。另外GetDC函数只能用在其他消息中,不能用在WM_PAINT窗口重绘消息中

      break;   

    case WM_CLOSE:

      if(IDYES==MessageBox(hwnd,"是否退出程序?","标题",MB_YESNO));

      {

        DestroyWindow(hwnd); //这个函数会销毁窗口并发送一个WM_DESTROY和WM_NCDESTROY消息,从而转到下面的case语句

      }

      break;

    case WM_DESTROY:

      PostQuitMessage(0);  //向消息队列中投递一个WM_QUIT消息,从而是while循环退出。参数0是作为WM_QUIT消息的附加参数,意义不大

      break;

    default:

      return DefWindowProc(hwnd,uMsg,wParam,lParam);  //对其他的喜爱系进行缺省处理,不可缺少,否则窗口可能不显示出来。

   }

  return 0;

}

转载于:https://www.cnblogs.com/hnfxs/archive/2013/06/13/3133405.html

### RT-DETRv3 网络结构分析 RT-DETRv3 是一种基于 Transformer 的实时端到端目标检测算法,其核心在于通过引入分层密集正监督方法以及一系列创新性的训练策略,解决了传统 DETR 模型收敛慢和解码器训练不足的问题。以下是 RT-DETRv3 的主要网络结构特点: #### 1. **基于 CNN 的辅助分支** 为了增强编码器的特征表示能力,RT-DETRv3 引入了一个基于卷积神经网络 (CNN) 的辅助分支[^3]。这一分支提供了密集的监督信号,能够与原始解码器协同工作,从而提升整体性能。 ```python class AuxiliaryBranch(nn.Module): def __init__(self, in_channels, out_channels): super(AuxiliaryBranch, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(self.conv(x))) ``` 此部分的设计灵感来源于传统的 CNN 架构,例如 YOLO 系列中的 CSPNet 和 PAN 结构[^2],这些技术被用来优化特征提取效率并减少计算开销。 --- #### 2. **自注意力扰动学习策略** 为解决解码器训练不足的问题,RT-DETRv3 提出了一种名为 *self-att 扰动* 的新学习策略。这种策略通过对多个查询组中阳性样本的标签分配进行多样化处理,有效增加了阳例的数量,进而提高了模型的学习能力和泛化性能。 具体实现方式是在训练过程中动态调整注意力权重分布,确保更多的高质量查询可以与真实标注 (Ground Truth) 进行匹配。 --- #### 3. **共享权重解编码器分支** 除了上述改进外,RT-DETRv3 还引入了一个共享权重的解编码器分支,专门用于提供密集的正向监督信号。这一设计不仅简化了模型架构,还显著降低了参数量和推理时间,使其更适合实时应用需求。 ```python class SharedDecoderEncoder(nn.Module): def __init__(self, d_model, nhead, num_layers): super(SharedDecoderEncoder, self).__init__() decoder_layer = nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead) self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers) def forward(self, tgt, memory): return self.decoder(tgt=tgt, memory=memory) ``` 通过这种方式,RT-DETRv3 实现了高效的目标检测流程,在保持高精度的同时大幅缩短了推理延迟。 --- #### 4. **与其他模型的关系** 值得一提的是,RT-DETRv3 并未完全抛弃经典的 CNN 技术,而是将其与 Transformer 结合起来形成混合架构[^4]。例如,它采用了 YOLO 系列中的 RepNCSP 模块替代冗余的多尺度自注意力层,从而减少了不必要的计算负担。 此外,RT-DETRv3 还借鉴了 DETR 的一对一匹配策略,并在此基础上进行了优化,进一步提升了小目标检测的能力。 --- ### 总结 综上所述,RT-DETRv3 的网络结构主要包括以下几个关键组件:基于 CNN 的辅助分支、自注意力扰动学习策略、共享权重解编码器分支以及混合编码器设计。这些技术创新共同推动了实时目标检测领域的发展,使其在复杂场景下的表现更加出色。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值