第1章,[标签 Win32] :第一个 Win32 程序,程序入口

专栏导航

上一篇:第1章:第一个 WIn32 程序,头文件

回到目录

下一篇:第1章 :第一个 WIn32 程序,MessageBox 函数

本节前言

本节的学习,需要以上一节作为先修知识。如果还没有去看本专栏的上一节,请你先去学习上一节的内容。

上一节内容的链接如下。

 第1章:第一个 WIn32 程序,头文件

在你学习过了上一节的基础上,我们来看本节的内容。

本节,我们要去讲解的,是程序入口。

一.    本节代码

首先呢,我们还是将本节的代码给贴出来。

#include <Windows.h>
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    MessageBox(NULL, TEXT("这是一个 Windows SDK 程序"),
        TEXT("WIndows SDK"), 0);
 
    return 0;
}

本节,我们并不去新建解决方案,而是采用本节前言里面的链接文章中,已经建立好的解决方案。

二,    参考代码

为了讲解本节代码,我们还需要给出参考代码。

参考代码如下。

#include <stdio.h>
 
int main(void)
{
    printf("这是一个控制台程序。\n");
    return 0;
}

三.    程序入口

在参考代码里面,我们所写的程序代码,是 C/C++ 控制台程序代码。在 C/C++ 控制台程序里面,程序入口为 main 函数。

在 Win32 程序里面,也有一个程序入口,它类似于 C/C++ 控制台程序中的 main 函数,是程序的入口点。然而,它的函数名不叫作 main,而是叫做 WinMain 。

四.    函数头部

我们来看一下本节代码中,WinMain 函数的头部代码。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)

本节,我们要来讲解的,就是这个东西了。

在查尔斯·佩措尔德的 Windows 编程教材中,WinMain 函数的声明形式如下。

int
WINAPI
WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    );

在我写作的当前,WinMain 函数的声明形式如下。

int __clrcall WinMain(
  [in]           HINSTANCE hInstance,
  [in, optional] HINSTANCE hPrevInstance,
  [in]           LPSTR     lpCmdLine,
  [in]           int       nShowCmd
);

在这里,我们还是以佩措尔德先生所提到的声明代码为例,进行讲解。

无论是佩措尔德版本,还是当前的版本,WinMain 函数的声明,都位于 Winbase.h 头文件里面。

五.    PSTR 与 LPSTR,长指针的概念

在佩措尔德先生的教材中,他对 WinMain 的函数头部作了一点改动。第三个参数在 Winbase.h 中的数据类型为 LPSTR,而在佩措尔德的代码中,也包括在我们的本节代码中,第三个参数的类型都是写作 PSTR。这两种数据类型在 Winnt.h 中都被声明为指向字符串的指针。

字符串,就是 string。其前三个字母,为 str,大写形式就是 STR 。指向字符串的指针,不就可以将其称作是 PSTR 吗?P 代表 pointer,指针的意思。那么,LPSTR 中的 LP 是什么意思呢?它是长指针的意思。长指针,英文为 long pointer 。

Windows 有过 Windows 95,Windows 98 这样的版本,它们是 16 位的 Windows 系统。16 位的Windows里面,要区分指针与长指针的概念。在那个时候,大概,int 数据类型,它也不是一个 4 字节的数据类型,而是一个 2 字节的数据类型,而 long 型整数,才是 4 字节的类型。

从 32 位 Windows 操作系统开始,就不去区分指针与长指针的概念了。因为,32 位系统里面,int 是 4 字节的数据类型。

既然不去区分指针与长指针的概念了,那么,自然地,LPSTR 与 PSTR,它们俩就是一个意思了。

同理,以后,我们还会接触到 LPCSTR 和 PCSTR 两种数据类型。这俩类型除了前缀 P 与 LP 的区别以外,后面的四个字母 CSTR 都是一样的。所以呢,从 32 位 Windows 操作系统开始,LPCSTR 和 PCSTR 也都是一个意思。 

在我写作本文的时候,我所用的 Windows 的版本是 Windows 11 。这个时候,PSTR 与 LPSTR,同样是一个意思。而 PCSTR 与 LPCSTR 也是一个意思。

在这里,LPCSTR 是什么意思?这里,我先不去讲。以后,我们会讲到的。

六.   匈牙利标记法,两个改动的变量名

佩措尔德先生还改动了 WinMain 头部中的两个变量名。

在绝大部分的 Windows 程序中,在命名变量的时候,都普遍采用匈牙利标记法。在匈牙利标记法里面,变量名前面会有一个短的前缀,用以表示这个变量的数据类型。以后,我们会去详细讨论匈牙利标记法。

在本节,我们需要了解的是,一般地,用前缀 i 或者 n 表示 int,用前缀 sz 表示 【以零结尾的字符串】。其中 i 和 n 分别为 int 的第一个字母和第二个字母。而在佩措尔德先生的代码里,他习惯用前缀 i 来标识 int 类型的变量。

实际上,在 printf 里面,但凡是【%d】出现的地方,都可以将其换成【%i】,你可以自己尝试一下。可见,用 i 来表示 int 类型,算是有它的依据的。

七.    调用约定,标准调用

我们还是来看一下 WinMain 的函数头部。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)

在这个东西里面,最左边的 int,表示说,此函数的返回值为 int 类型。第三个东西,WinMain 是函数名,再往后,就是形参列表了。这些我们都懂。

可是,WINAPI,这个东西是啥?

它是一个宏代码。在 Windef.h 里面,用以下语句来定义 WINAPI 标识符。

#define WINAPI __stdcall

这条宏代码,它是让 WINAPI 等价于 __stdcall 。 __stdcall,是调用约定的一种,叫做标准调用。

在 C/C++ 中,有很多的调用约定。我们在基础的 C/C++ 学习阶段里,所使用的各种函数,无论是系统自带的 printf 也好,还是我们自己定义的各种函数也好,所用的调用约定,为 CDECL 约定。

在 Windows 里面,用 __cdecl 来标识 CDECL 调用约定。

绝大多数的 Windows 函数调用,都被定义成 WINAPI,也就是都采用了名为【标准调用】的调用约定。

至于说,标准调用和 CDECL 约定之间有何区别,我们在这里不展开。实际上,我对此也了解得不多。如果在这里,我稍微展开一点来讲解的话,那就会涉及汇编语言层面的知识了。

本专栏假定大家尚未学习过汇编语言,因此,对于调用约定的知识,大家需要知道的是,标准调用和 CDECL 是两种不同的调用约定。如果未明确标识的话,则大家自定义的函数,都采用的是 CDECL 调用约定。而 Windows API 中的大多数函数,都采用的是标准调用。 

八.    形参列表

(一)前两个形参,句柄

形参列表中的前两个形参的数据类型,均为 HINSTANCE 。它的含义是:实例句柄。英文为 Instance Handle 。

形参列表中的第一个参数的变量名,为 hInstance,它代表的是,当前实例句柄。

形参列表中的第二个参数的变量名,为 hPrevInstance,它代表的是,前一个实例的句柄。

我们算是第一次在专栏中提到句柄这个概念。

句柄,在 Windows 程序中,它是一个很重要的概念。

句柄,它是一个标识 WIndows 资源(比如菜单,图标,窗口等)和设备等的指向地址(无符号的 32 位整数)。通常用它来标识系统中的一个内核(资源)对象。

在我们的本节代码里面,当前实例句柄 hInstance 就唯一地标识了我们的这个程序。实际上,在某些 Windows 程序中,把句柄当作调用参数是必须的。

比如在早期的 Windows 版本中,当多路并发运行同一个程序时,就需要为那个程序创建多个实例。同一程序的所有的实例都共享代码以及只读存储(即菜单和对话框模板之类的资源)。一个程序可以通过查看 hPrevInstance 参数,从而知道是否有它的其他实例正在运行。它也就可以因此跳过某些零散杂务步骤,把一些数据从前一个实例拷贝到自己的数据区域里面。

从 32 位 Windows 操作系统开始,已经不再采用【前一个实例的句柄】这一概念,也就是,hPrevInstance 变量不再使用。因此,WinMain 的第二个参数,hPrevInstance,通常总是 NULL(定义为 0)。

(二)第三个参数,命令行

WinMain 的第三个参数是用来运行程序的命令行。有些 Windows 程序在启动时用它来把文件装入内存。

(三)第四个参数,显示方式

WinMain 的第四个参数用来指示程序最初的显示方式。在程序刚开始运行的时候,可以采用最大化显示的方式,可以正常显示,可以最小化到任务栏上。我们在以后来细讲这个参数。

九.    学习方法提示

本节所涉及的一些个概念,有可能,你并不能够彻底地理解。比如,调用约定,有可能,你就未能够很好地理解。再比如,句柄,命令行装入文件,这样的概念,你也有可能未能够彻底地理解它们。

我是觉得,对于这样的一些个东西,暂时地,你也不需要去彻底了解。有个概念,混个脸熟就可以了。

在以后的学习中,我们还会多次用到这些个东西,暂时你无须深究,会用就可以了。暂时地,你也不需要举一反三,举一反一就行了。

结束语

本节的内容还是不少的。

到本节为止,第一个 Win32 程序,我们依然是没有讲完。

下一节,我们会继续讲解这个 Win32 程序。

本节结束。

专栏导航

上一篇:第1章:第一个 WIn32 程序,头文件

回到目录

下一篇:第1章 :第一个 WIn32 程序,MessageBox 函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值