0x01 译文:Windows桌面应用Win32开发简介

摘要:
本课将简要介绍使用C++开发Windows桌面应用程序的一些基本知识目录:准备开发环境Windows代码规范操作字符串什么是窗口?Windows API提供的C++类很少。其中一些重叠只是由于Windows API的历史。当需要解析内存范围外的当前段时,长指针从16位Windows延迟。LP前缀是保留的,这使得将32位Windows的16位代码迁移到32位Windows更加容易。当32位应用程序在64位Windows上运行时,这些数据类型仍为4字节宽。Windows对Unicode字符使用UTF-16编码,其中每个字符编码为16位值。

本节课将简单介绍下使用C++开发Windows桌面应用的一些基础知识

 目录:

准备你的开发环境

Windows 代码规范

操作字符串

什么是一个Window?

WinMain:程序的入口点

1. 准备你的开发环境

安装 Windows SDK

       要用C或者C++开发Windows 程序,你必须安装 Microsoft Windows Software Development Kit (SDK)  或者一个包括Windows SDK的开发环境。

这个Windows SDK 包含了头文件和编译链接你的程序所需的类库,这个Windows SDK 也包括Visual C++ 编译和链接命令行工具。

尽管你用这个命令行工具可以编译和链接运行你的程序,但是还是建议你安装一个功能比较齐全的Microsoft Visual Studio.

Visual C++ Express 是一个免费的可下载的Visual C++ 软件,Visual Studio Community是 Visual Studio Express 的更新替代产品。

Windows SDK 集成IDE 下载(官方推荐):https://www.visualstudio.com/zh-hans/vs/visual-studio-express/

Tips:

这个Windows SDK 不支持硬件驱动的开发,在这个也不作讨论。

如果想学习这方面的开发,请移步https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/index

 Microsoft Visual C++ 2010 Express(已过时)是微软推出的一款免费软件,它比Microsoft Visual C++ 6.0 Enterprise Edition的兼容性和标准支持更好,比Microsoft Visual Studio 2010 Ultimate的体积更小。

 如果你对使用Visual C++ 2010 Express感兴趣,可以移步查看详细教程:https://msdn.microsoft.com/en-us/library/ff742833.aspx

Windows代码规范

如果你是一个开发Windows程序的新手,当你第一次看到一个WIndows程序的时候你可能会感到困惑。

因为这个代码往往会出现像DWORD_PTR 和 LPRECT 一样充满了奇怪的类型定义和一些像hWnd 和 pwsz 变量(这称为匈牙利命名法)。学习这些代码规范是值得的。

绝大多数Windows API由函数或 Component Object Model(COM)组件对象模型接口组成。

很少有Windows API提供的C++类。(一个显著的例外是GDI+,一种二维图形API)

Windows的头文件包含很多类型,这些很多都定义在头文件WinDef.h 中,下面这些是你经常会遇到的。

0x01 译文:Windows桌面应用Win32开发简介第1张

你可以看到,在这些有一定数量的冗余类型。其中一些重叠仅仅是由于Windows API的历史。这里列出的类型有固定的大小,在32位和64个应用程序中的大小相同。例如,DWORD类型总是32位的

Boolean Type

BOOL是一个整数类型的值,用于布尔上下文定义。头文件 WinDef.h 也使用bool值定义了两个。

BOOL is a typedef for an integer value that is used in a Boolean context. The header file WinDef.h also defines two values for use with BOOL

#define FALSE    0 
#define TRUE     1

尽管布尔类型TRUE可以定义为任何一个非0的数字,但是你最好遵守规范总是TRUE设置为1

正确的方式

// Right way.
BOOL result = SomeFunctionThatReturnsBoolean();
if (result) 
{ 
    ...
}

错误的方式

// Wrong!
if (result == TRUE) 
{
    ... 
}

Tips:注意BOOL是一个整数型且不可与C++ bool型互换。

指针类型:

 Windows定义了很多表格 pointer-to-X 类型. 他们通常在名字的前面有前缀  P- or LP- .例如, LPRECT 是一个指向矩形的指针类型 , 这个矩形是一个描述了一个矩形的结构体.

下面这样是等价的

RECT*  rect;  // Pointer to a RECT structure.
LPRECT rect;  // The same
PRECT  rect;  // Also the same.

历史上,P代表“指针”,LP代表“长指针”。长指针(也叫远指针)从16位Windows的延期,当他们需要解决内存范围以外的当前段。保留了LP前缀,使它更容易将32位Windows的16位代码移植到32位窗口中。

但是今天没有区别——指针就是指针。

 指针精度类型

以下数据类型始终是指针的大小,即32位应用程序中的32位宽,64位应用程序中的位宽为64位。大小是在编译时确定的。

当32位应用程序在64位Windows上运行时,这些数据类型仍为4字节宽。(64位应用程序不能在32位Windows上运行,因此不会出现相反的情况。)

  • DWORD_PTR
  • INT_PTR
  • LONG_PTR
  • ULONG_PTR
  • UINT_PTR

 These types are used in situations where an integer might be cast to a pointer. They are also used to define variables for pointer arithmetic and to define loop counters that iterate over the full range of bytes in memory buffers. More generally, they appear in places where an existing 32-bit value was expanded to 64 bits on 64-bit Windows.

在将整数转换为指针的情况下使用这些类型。它们还用于定义指针算术的变量,并定义循环计数器,迭代内存缓冲区中的所有字节。更一般地,它们出现在64位窗口上现有32位值被扩展到64位的地方。

Hungarian Notation 匈牙利命名法

匈牙利符号是将前缀添加到变量名中的实践,以便提供有关变量的附加信息。(符号的发明者查尔斯·西蒙尼是匈牙利人,因此得名)。

 操作字符串

Windows本身支持UI元素,Unicode字符串的文件名,等等。Unicode是首选字符编码,因为它支持所有字符集和语言。Windows是Unicode字符使用UTF-16编码,其中每个字符被编码为一个16位的值。UTF-16字符称为宽字符,以区别于8位ANSI字符。Visual C++编译器支持宽字符的内置数据类型

WinNT.h  头文件中也定义了以下类型

typedef wchar_t WCHAR;

你会看到在MSDN的例子代码的两个版本。若要声明宽字符文字或宽字符串文字,请将 L 置于文字之前。

wchar_t a = L'a';
wchar_t *str = L"hello";

这里有一些其他的字符串相关的定义,你会看到:

0x01 译文:Windows桌面应用Win32开发简介第2张

Unicode和ANSI函数

 当微软向Windows引入Unicode支持时,它通过提供两组并行API,一个用于ANSI字符串,另一组用于Unicode字符串,从而缓解了过渡。

例如,设置窗口标题栏的文本有两个函数:

SetWindowTextA //takes an ANSI string.
SetWindowTextW //takes a Unicode string.

 实际上真正使用的时候会加上判断这样定义

#ifdef UNICODE
#define SetWindowText  SetWindowTextW
#else
#define SetWindowText  SetWindowTextA
#endif 

 在MSDN中,函数的名字记录SetWindowText函数下,即使那是真的宏的名字,不是实际的函数名。

新的应用程序应该总是调用Unicode版本。许多世界语言都需要Unicode。如果使用ANSI字符串,将不可能本地化应用程序

ANSI版本的效率也较低,因为操作系统必须在运行时将ANSI字符串转换为Unicode。

根据你的喜好,你可以调用Unicode功能明确,如setwindowtextw,或者使用宏。MSDN上的示例代码通常会调用宏,但两种形式是完全等价的。Windows中大多数更新的API只有Unicode版本,没有相应的ANSI版本

 当应用程序需要同时支持Windows NT以及Windows 95、Windows 98和Windows Me 时,根据目标平台编译ANSI或unicode字符串相同的代码是非常有用的。

为此,Windows SDK提供了将字符串映射到Unicode或ANSI的宏,这取决于平台

0x01 译文:Windows桌面应用Win32开发简介第3张

下面是个例子:

SetWindowText(TEXT("My Application"));

解析如下:

SetWindowTextW(L"My Application"); // Unicode function with wide-character string.

SetWindowTextA("My Application");  // ANSI function.

TEXT 和 TCHAR  宏今天已经很少用了,因为所有的应用程序都应该使用Unicode ,然而你可能在MSDN看到一些这样的代码:

然而,你可能会在旧的代码和一些MSDN代码例子看到他们。
微软C运行时库的标题定义了一组类似的宏

#ifdef _UNICODE
#define _tcslen     wcslen
#else
#define _tcslen     strlen
#endif 

 注意:有些标题使用预处理器符号Unicode,别人用_unicode使用下划线前缀。总是定义这两个符号。当你创建一个新项目,Visual C++将它们通过默认的方式设置。

 什么是Window?

 显然,Windows是Windows的中心。它们是如此重要,以至于他们在操作系统之后命名了操作系统。

但是什么是窗口?当你想到一个窗口,你可能会想到这样的东西:

0x01 译文:Windows桌面应用Win32开发简介第4张

这种类型的窗口称为应用程序窗口或主窗口。它通常有一个标题栏、最小化和最大化按钮以及其他标准UI元素的框架。

该框架称为窗口的非客户端区域,之所以称为“窗口”,是因为操作系统负责管理窗口的那部分。框架内的区域是客户区。这是您的程序管理的窗口的一部分。

这里是另一种类型的窗口:

0x01 译文:Windows桌面应用Win32开发简介第5张

如果您是Windows编程新手,您可能会惊讶于UI控件,如按钮和编辑框本身就是Windows。UI控件和应用程序窗口之间的主要区别在于控件本身并不存在。

相反,控件相对于应用程序窗口定位。当拖动应用程序窗口时,控件将随它移动,如您所期望的那样。另外,控件和应用程序窗口可以相互通信。

(例如,应用程序窗口从按钮接收单击通知。)

因此, 当你考虑window时, 不要把它简单地认为是个应用窗口,取而代之的是一种编程结构:

占据屏幕的某一部分。
在某一时刻可能不可见。
知道如何画自己。
响应来自用户或操作系统的事件。

Parent Windows and Owner Windows 父窗口和所有者窗口

在UI控件的情况下,控件窗口被称为应用程序窗口的子窗口。应用程序窗口是控件窗口的父窗口。父窗口提供用于定位子窗口的坐标系统。有父窗口会影响窗口外观的各个方面;例如,子窗口被裁剪,这样子窗口的任何部分都不能出现在其父窗口的边框外面。
另一种关系是应用程序窗口和模式对话框窗口之间的关系。当应用程序显示一个模态对话框时,应用程序窗口是所有者窗口,而对话框是一个拥有窗口。拥有的窗口总是出现在它的所有者窗口前面。当所有者被最小化时,它被隐藏,并在所有者被销毁的同时被销毁。
下图显示了一个应用程序,其中显示一个带有两个按钮的对话框:

0x01 译文:Windows桌面应用Win32开发简介第6张

这个应用窗口有自己的对话框窗口,这个对话框窗口是父窗口中的两个按钮

下面是这两者之间的关系:

0x01 译文:Windows桌面应用Win32开发简介第7张

Window Handles

窗口对象既有代码和数据,但他们都不是C++类。相反,程序使用一个称为Handle(句柄)的值引用一个窗口

一个Handle 是一个不透明类型。本质上,它只是操作系统用来标识对象的一个数字。您可以将窗口视为创建所有窗口的大表。它使用此表通过Hanlder查找窗口。(无论是它是如何工作的内部是不重要的。)

窗口的数据类型Hanlder是一个HWND,这通常是明显的“aitch-wind“。窗口句柄是由函数返回:CreateWindowCreateWindowEx创建窗口。

在一个窗口进行操作,你通常会调用一些函数有一个窗口的句柄值作为参数。例如,复位窗口在屏幕上,调用MoveWindow函数:

BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint);

 第一个参数是要移动的窗口的句柄。其他参数指定窗口的新位置和窗口是否应该重绘.

记住句柄不是指针。如果窗口是一个变量,包含一个hanlder,试图引用句柄HWND  *hwnd 这样写是错误的。

屏幕和窗口坐标

坐标是以设备无关的像素来度量的。在讨论图形时,我们将更多地讨论与设备无关的像素的与设备无关的部分。
根据您的任务,您可以测量相对于屏幕的坐标,相对于窗口(包括帧),或相对于窗口的客户端区域。

例如,您将使用屏幕坐标在屏幕上定位窗口,但您将使用客户机坐标在窗口中绘图。在每种情况下,原点(0, 0)总是区域的左上角。

0x01 译文:Windows桌面应用Win32开发简介第8张

WinMain: 程序入口点

每一个Windows程序,都有一个入口函数,叫做WinMainwWinMain

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);

这四个参数的意义:

hInstance :          是一种叫做“实例的句柄”或“处理模块”的操作系统使用这个值来确定可执行文件(EXE)时,在内存中加载。实例句柄是某些Windows功能-例如,加载图标或位图。

hprevinstance:    没有意义。它在16位窗口中使用,但现在总是0。

pcmdline:            包含命令行参数作为一个Unicode字符串。

ncmdshow:          是一个标志,是应用程序的主窗口将被最小化,最大化,或显示正常。

函数返回一个int类型的值。操作系统不使用该返回值,但你可以使用返回值将状态代码发送给您编写的其他程序。

WINAPI 的调用约定。调用约定定义函数如何从调用者接收参数。例如,它定义了参数出现在堆栈上的顺序。只要确保你wWinMain函数如下声明。

WinMain 函数和 wWinMain是相同的,唯一不同的是命令行参数作为一个ANSI字符串,Unicode 版本是首选。

你可以使用ANSI WinMain 函数,尽管你编译的程序是以Unicode编码。要得到一个Unicode字符编码的命令行参数,可以调用 GetCommandLine 函数。

这个函数将返回一个单一的字符串,如果你想作为一个参数式阵列的参数,通过这个字符串CommandLineToArgvW

CRT内部主要做一些额外的工作。例如,静态初始化器是之前调用wWinMain。虽然可以告诉链接器使用不同的入口点函数,但如果链接到CRT,则使用默认值。

否则,将跳过CRT初始化代码,结果不可预测。(例如,全局对象初始化不正确)。

这是一个空的函数

INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR lpCmdLine, INT nCmdShow)
{
    return 0;
}

现在你已经拥有了入口点并理解了一些基本术语和编码约定,下篇博文我们就可以创建一个完整的窗口程序了。

免责声明:文章转载自《0x01 译文:Windows桌面应用Win32开发简介》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇libpcap编程实例CanvasRenderingContext2D 整理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ABAP 用指针的方式

首先要定义指针 FIELD-SYMBOLS: <fs_out> LIKE gt_tab_out. 然后在循环赋值时使用ASSIGNING关键字对指针初始化。 LOOP AT lt_tab_out ASSIGNING <fs_out> WHERE aufnr NE space.        READ TABLE lt_afko WI...

go第三方日志系统-seelog-Basic sections

https://github.com/cihub/seelog 文档学习:https://github.com/cihub/seelog/wiki 1.安装: go get github.com/cihub/seelog 2.快速启动 Seelog的设计非常方便。它的默认配置和包级别的日志记录器是现成的,所以开始你只需要两行代码: package main...

C++ 系列:虚函数

Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明。 ---------------------------------------------------------------------------------...

oracle instr查询字符串

INSTR   (源字符串, 目标字符串, 起始位置, 匹配序号)   在Oracle/PLSQL中,instr函数返回要截取的字符串在源字符串中的位置。只检索一次,就是说从字符的开始   到字符的结尾就结束。   语法如下:   instr( string1, string2 [, start_position [, nth_appearance ] ]...

js中setTimeout和setInterval的应用方法(转)

JS里设定延时: 使用SetInterval和设定延时函数setTimeout 很类似。setTimeout 运用在延迟一段时间,再进行某项操作。 setTimeout("function",time) 设置一个超时对象 setInterval("function",time) 设置一个超时对象 SetInterval为自动重复,setTimeout不会重...

oracle 在sql中显示blob的字符串

最近在用oracle的过程中用到了对blob字段模糊查询的问题,对oracle来说,我并不是高手,找了很多的资料终于能够查出来了。blob字段直接用 select * from table_name where column like ‘%%’查找的时候是不能实现的 ,主要是字段类型不符,就想到了字段转换成varchar2类型,然后再进行查询select...