Windows编程基础,第一个Windows程序,注册窗口,创建窗口

Win32编程,指的是用Window提供的API(Application Programming Interface)为Window编写应用程序。Windows编程,大家也通常称为win32编程,或win32 SDK编程,其含义是针对32位Windows操作系统。
在这一章节中,我们会带大家认识Windows应用程序,编写第一个Windows程序,带大家学习注册窗口和创建窗口。

一.Windows应用程序分类

在Windows平台下,我们可以将应用程序大致分为一下几类:

  • 控制台程序
  • 窗口程序
  • 库程序(静态库.lib,动态库.dll)

二.开发工具和库

开发工具我们现在大多数都是用visual studio,visual studio开发平台集成了很多工具,非常方便了我们使用,这里我来带领大家看看编译程序到底是如何实现的:

  • 编译器cl.exi,可以将我们写好的.c或者.cpp编译成目标代码.obj文件
  • 链接器LINK.EXE,将目标代码,库链接生成最终文件
  • 资源编译器RC.EXE(编译.rc文件<脚本语言代码>),将资源(应用程序图标等)编译,最终通过链接器生成最终文件。
    接下来带大家看看几个重要的库文件:
    Windows库:
  • Kernel32.dll:提供核心API,例如进程,线程,内存管理
  • user32.dll:提供了窗口,消息等API
  • gdi32.dll:绘图相关API
    库和头文件
  • windows.h:所有Windows头文件的集合
  • windef.h:Windows数据类型
  • winbase.h:kernel32的API
  • wingdi.h:gdi32的API
  • winuser.h:user32的API
  • winnt.h:UNICODE字符集支持

三.程序编译过程

  • 环境准备:vcvars32.bat
  • 编译程序:cl.exe xxxxxx.c xxxxxxx.cpp
  • 链接程序:LINK.EXE xxxxxx,obj xxxxx.lib
  • 编写资源.rc资源脚本文件
  • 编译.rc文件:rc.exe xxx.res
  • 将资源链接到程序中
    这里给出一张图帮助大家理解:
    程序编译过程

四.第一个Windows程序

// Win32VS版本.cpp : 定义应用程序的入口点。
#include "framework.h"
#include "Win32VS版本.h"
#define MAX_LOADSTRING 100
LRESULT CALLBACK WindowProc(
	IN  HWND hwnd,
	IN  UINT uMsg,
	IN  WPARAM wParam,
	IN  LPARAM lParam
);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    //窗口类名
    TCHAR className[] = TEXT("My First Windows.");

    //创建窗口对象
    WNDCLASS wndclass = { 0 };
    wndclass.hbrBackground = (HBRUSH)0;            //窗口背景颜色
    wndclass.lpfnWndProc = WindowProc;                    //窗口的过程函数
    wndclass.lpszClassName = className;                     //窗口的类名字
    wndclass.hInstance = hInstance;                 //定义窗口类的应用程序的实例句柄

    //注册窗口
    RegisterClass(&wndclass);

    //创建窗口
    HWND hwnd = CreateWindow(
        className,                         //lpname类名
        TEXT("我的第一个窗口。"),          //窗口标题
        WS_OVERLAPPEDWINDOW,               //dwStyle
        10,
        10,
        600,
        300,
        NULL,
        NULL,
        hInstance,
        NULL);

    //显示窗口
    ShowWindow(hwnd, SW_SHOW);

    // 主消息循环:
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd,NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}
LRESULT CALLBACK WindowProc(
	IN  HWND hwnd,
	IN  UINT uMsg,
	IN  WPARAM wParam,
	IN  LPARAM lParam
)
{
	switch (uMsg)
	{
		//窗口消息							
	case WM_CREATE:
	{

		CREATESTRUCT* createst = (CREATESTRUCT*)lParam;


		return 0;
	}
	case WM_MOVE:
	{

		POINTS points = MAKEPOINTS(lParam);


		return 0;
	}
	case WM_SIZE:
	{

		int newWidth = (int)(short)LOWORD(lParam);
		int newHeight = (int)(short)HIWORD(lParam);


		return 0;
	}
	case WM_DESTROY:
	{

		PostQuitMessage(0);

		return 0;
	}
	//键盘消息							
	case WM_KEYUP:
	{
		return 0;
	}
	case WM_KEYDOWN:
	{
		return 0;
	}
	//鼠标消息							
	case WM_LBUTTONDOWN:
	{
		POINTS points = MAKEPOINTS(lParam);
		return 0;
	}
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

在这里向大家介绍一下编写一个Windows程序必要的步骤:

  • 1.定义程序入口WinMain函数
  • 2.定义窗口处理函数(可以自定义,作用:处理消息)
  • 3.注册窗口类(向操作系统内核中写入一段数据)
  • 4.创建窗口(在内存中创建窗口)
  • 5.显示窗口(绘制窗口图像)
  • 6.消息循环(获取,翻译,派发)
  • 7.消息处理

五.注册窗口类

1.窗口类的概念:

<1>.窗口类包含了窗口各种参数信息的结构
<2>.每个窗口都有一个窗口类,基于窗口类创建窗口
<3>.每个窗口类都有一个名称,使用前必须注册到操作系统内核中

2.窗口的分类

<1>.系统窗口类
系统已经定义好的窗口类,所有应用程序都可以使用,例如Button(按钮),edit(编辑框)
<2>.应用程序全局窗口类
由用户自己定义,当前应用程序所有模块中都可以使用
<3>.应用程序局部窗口类
由用户自己定义,当前应用程序中只有本模块可以使用

3.注册窗口类函数

ATOM RegisterClassExW(
  [in] const WNDCLASSEXW *unnamedParam1
);

在这里带大家看一下WNDCLASS结构体:

typedef struct tagWNDCLASSA {
  UINT      style;               //窗口风格
  WNDPROC   lpfnWndProc;         //窗口处理函数
  int       cbClsExtra;          //窗口类的附加数据buff的大小
  int       cbWndExtra;          //窗口类的附加数据buff的大小
  HINSTANCE hInstance;           //当前模块的实例句柄
  HICON     hIcon;               //窗口图标句柄
  HCURSOR   hCursor;             //鼠标句柄
  HBRUSH    hbrBackground;       //绘制窗口背景的画刷句柄
  LPCSTR    lpszMenuName;        //窗口菜单的资源id字符串
  LPCSTR    lpszClassName;       //窗口类的名称
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

大家也可以在微软的MSDN官方文档中查看:WNDCLASS结构体

4.style窗口类风格

应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALECLASS
应用程序局部窗口类,在注册窗口类时,不添加CS_GLOBALECLASS
具体的窗口类风格,大家可以到MSDN官方文档中查看:窗口类风格style

六.创建窗口

我们来看看创建窗口函数:

HWND CreateWindowExW(
  [in]           DWORD     dwExStyle,          //窗口的拓展风格
  [in, optional] LPCWSTR   lpClassName,        //已经注册的窗口类名称
  [in, optional] LPCWSTR   lpWindowName,       //窗口标题栏名称
  [in]           DWORD     dwStyle,            //窗口的基本风格
  [in]           int       X,
  [in]           int       Y,
  [in]           int       nWidth,
  [in]           int       nHeight,            //以上四个参数是决定窗口的位置
  [in, optional] HWND      hWndParent,         //父窗口句柄
  [in, optional] HMENU     hMenu,              //窗口的菜单句柄
  [in, optional] HINSTANCE hInstance,          //当前应用程序实例句柄
  [in, optional] LPVOID    lpParam             //窗口创建时的附加参数
);

这里给出MSDN官方文档地址:CreateWindowExW函数
那么在CreateWindow函数中,具体是如何实现的?
这里先给出一张图,我们围绕这张图来讲解:
系统内核

CreateWindow函数创建窗口过程:

  • 1:CreateWindow函数内部根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到与之匹配的,执行2,如果没有找到,执行3
  • 2:比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果相等,说明创建和注册的窗口在同一个模块,创建窗口并返回
  • 3:在应用程序全局窗口类中查找窗口类名称,如果找到,执行4,否则执行5(这时候已经不需要比对hins参数了,因为之前提到了,应用程序全局窗口类当前应用程序任何一个模块都可以使用
  • 4:使用找到的窗口类信息,创建窗口并且返回
  • 5:在系统窗口类中查找,如果找到,创建窗口并且返回(这时候已不需要比对hins参数,理由同上),如果未找到,创建创库失败,返回0。
    这里给大家一段伪代码,表示创建窗口的过程:
CreateWindow(各种参数){
	匹配查找窗口类,即上述过程
	if(找到了){
		申请一块内存,将窗口的数据存该内存(该数据包括两部分,CreateWindow函数的参数和找到的窗口类信息)
		return 本内存句柄;
	}else{
	return 0;
	}

七.无法正常关闭窗口问题

当我们写出第一个windows程序的时候,我们点击程序的关闭按钮,发现程序是不显示了,但是在任务管理器中仍然存在,说明我们的应用程序没有正常关闭。
我们来看看,为什么没有正常关闭?没有正常关闭的话,我们的程序停在了哪?
我们发现在代码的消息处理函数中有这样一段代码:

MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd,NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}

我们可以发现我们的程序肯定停在了while循环中,我们分析发现,如果GetMessage函数返回值为0,那么程序就可以正常关闭,那么我们如何让GetMessage函数的返回值变为零呢?
我们可以将窗口处理函数这样写:

switch(msgID){
	case WM_DESTORY:{
		PostQuitMessage(0);
		break;
	}
}

这样我们写的应用程序就可以正常退出了,至于这其中的原理,我们会在后续章节中讲解到。

八.子窗口创建过程

子窗口的创建很简单,我们只需要满足两个条件;

  • 1.创建窗口时设置父窗口句柄
  • 2.创建窗口时,在style参数中加入WS_WHILD|WS_VISIBLE即可。
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Shad0w-2023

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

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

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

打赏作者

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

抵扣说明:

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

余额充值