Windows基础知识

windows api

scss 复制代码
WinDef.h 基本数据类型
WinBase. 内核有关定义(kernel)
WinGdi.h 图形设备接口有关定义
WinUser.h 用户界面有关定义

WinMain

主函数

Windows程序入口函数

原型如下:

arduino 复制代码
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow );

WinMain函数接收4个参数,这些参数都是在系统调用WinMain函数时,传递给应用程序的。

hInstance:

  1. 表示该程序当前运行的实例句柄,是一个数值标识。当程序在Windows下运行时,它唯一标识运行中的实例(注意,只有运行中的程序实例,才有实例句柄)。一个应用程序可以运行多个实例,每运行一个实例,系统都会给该实例分配一个实例句柄,并通过hInstance参数传递给WinMain函数。
  2. 表示模块, 模块代表一个运行中的.exe或.dll文件, 表示这个文件的所有代码和资源, 磁盘上的文件不是模块,载入内存后运行时才成为模块.
  3. 每个模块都有一个唯一的模块句柄来标识,模块句柄实际上就是一个内存基地址,系统将.exe或.dll文件加载到地址空间的这个位置

hPrevInstance:

表示当前实例的前一个实例的句柄。通过查看MSDN我们可以知道,在Win32环境下,这个参数总是NULL,在Win32环境下,这个参数不再起作用

lpCmdLine

是一个以空字符结尾的字符串,内容为命令行的参数

nCmdShow

指定程序的窗口应该如何显示,例如最大化、最小化、隐藏等。这个参数的值由该程序的调用者所指定,应用程序通常不需要去理会这个参数的值。

MessageBox

对话框

arduino 复制代码
int WINAPI MessageBox(
    HWND hWnd, // 消息框的所有者(拥有者)的窗口句柄
    LPCTSTR lpText, // 要显示的消息内容
    LPCTSTR lpCaption, // 消息框的标题
    UINT uType, // 消息框的图标样式和暗流样式
)

MessageBox(NULL, TEXT("用户点击了按钮"), TEXT("caption"), MB_OK);
WinMain函数前的修饰符WINAPI,其实就是__stdcall。表示函数压栈方式和函数调用约定

字符编码ASCII, 扩展ASCII DBCS Unicode 和 ANSI

  • ASCII: 表示数字,大小写字母,标点符号,运算符号

  • 扩展ASCII: 在 ASCII基础上,支持框线,音标和其他欧洲非英语系字母

  • DBCS: 双字节字符集, 用2个字节表示除ASCII以外的字符,常见的就是我国的GB系列编码,也可以用双字节表示日语, 韩语等编码

  • Unicode: 表示不同国家和地区的语言, 只有一个字符集, 包含了世界上任何一个国家和地区的语言所用的字符, 定义了所有的主要语言中使用的字母,符号和标点.Unicode有三种编码形式,允许字符以字节,字和双字格式存储

    • UTF8 字符编码为1字节,有的字符编码为双字节,有的字符编码为3字节
    • UTF16 将每个字符编码为2字节,在谈到Unicode,除非特殊声明,都是指UTF16编码,因为固定位16字节,所以编码和计算长度速度快
    • UTF32 将每个字符都编码为4字节,用于不太关心存储空间的环境中,一般很少用到这种编码

    ANSI Unicode

    • ANSI 不同编码间不兼容,表示具体国家的多字节字符集
    • Unicode : 统一的字符集

字符和字符串处理

  • char: 单字节组成的字符
  • wchat_t: 宽字节,一般指UTF16, 统一用两字节来表示一个字符, 是现代计算机默认编码方式 占用空间比多字节多,但是处理速度快
ini 复制代码
wchat_t wc = L'A';
wchat_t *pwStr = L"Hello";
wchat_t szwStr[] = L"Hello";

sizeof

返回变量,对象或数据类型所占用的字节数

c 复制代码
char ch = 'A'; // 1
wchar_t wch = L'A'; // 2
char str[] = "c语言"; // 6, C 占用1字节,语言占用4字节,还有1字节结束符号
wchar_t wstr[] = L"C语言"; // 8, 一个字符占用两个字节, 还有2字节的字符串结束标志
printf("ch = %d, wch = %d, str = %d, wstr = %d\n", sizeof(ch), sizeof(wch), sizeof(str), sizeof(wstr));

TCHAR

Windows 在winnt.h 头文件中定义了自己的字符和宽字符数据类型

arduino 复制代码
typedef char CHAR: // 字符
ifndef _MAC
    typedef wchart_t WCHAR; // 宽字符
#else 
    // 苹果Mac 编译器没有定义wchar_t数据类型,宽字符被定义为16位整数
    typedef unsigned short WCHAR;
#endif

VS 创建项目的时候,默认使用unicode字符集, 右键点击项目名称->属性->配置属性->C/C++->命令行,可以看到UNICODE和_UNICODE都被定义了, 如果把项目属性设置为多字符字符集,这两个宏都会被取消定义

根据项目是否使用Unicode字符集, TCHAR被解释为CHAR(char)或WCHAR(wchar_t)数据类型

TEXT 宏

winnt.h头文件定义了宏

arduino 复制代码
#ifdef UNICODE
    #define __TEXT(quote) L##quote
#else 
    #define __TEXT(quote) quote
#endif

## 被称为令牌粘贴,表示把字母L和宏参数拼接到一起,假设quoto是Hello, L#quoto就是 L"Helllo"了, L表示宽字符

字符串数据类型

winnt.h 头文件中定义了许多字符串数据类型, 例如

arduino 复制代码
typedef char CHAR;
typedef CHAR *NPSTR,*LPSTR,*PSTR;
typedef CONST CHAR *LPCSTR,*PCSTR;
typedef WCHAR *NWPSTR, *LPWSTR,*PWSTR;
typedef CONST WCHAR *LPCWSTR, *PCWSTR;

#ifdef UNICODE
    typedef LPWSTR PTSTR,LPTSTR;
    typedef LPCWSTR PCTSTR,LPCTSTR;
#else 
    typedef LPSTR PTSTR,LPTSTR,PUTSTR,LPUTSTR;
    typedef LPCSTR PCTSTR,LPCTSTR,PCUTSTR,LPCUTSTR;

C表示const, 如果希望我们的程序支持ANSI和Unicode版本,通过编写两套代码分别实现ANSI和Unicode版本,有了这些宏,能实现对两种编码的通用编程(通用版本)

通用入口点函数

java 复制代码
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);

根据是否定义了UNICODE,入口点函数会被解释为

arduino 复制代码
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);

本书中都使用 WinMain,如果使用 _tWinMain, 那么必须包含 tchar.h

常用字符串处理函数

  1. 获取字符串长度 strlen wcslen(针对宽字符,返回宽字符数量)
arduino 复制代码
size_t strlen(const char *str); // char 类型字符串指针
size_t wcslen(const wchar_t *str); // wchar_t 类型字符串指针
scss 复制代码
CHAR str[] = "c语言"; // 5 C占用1个字节, 语言占用4字节
WCHAR wstr[] = L"C语言"; // 3 3个宽字符
_tprintf(TEXT("strlen(str) =%d, wcslen(wstr) = %d\n", strlen(str), wcslen(wstr)));

它两的通用版本_tcslen

arduino 复制代码
#ifdef _UNICODE
    #define _tcslen wcslen
#else 
    #define _tcslen strlen
#endif

查找字符串首次出现的指定字符

strchr(首次出现), 通用版本是 _tcschar strrchr(最后出现), 通用版本是 _tcsrchr

scss 复制代码
TCHAR szStr[] = TEXT("Windows API 最为强大的编程语言");
LPTSTR lp = _tcschr(szStr, TEXT('最'));
setlocale(LC_ALL, "chs");// 用_tprintf函数输出中文字符的时候,需要调用本函数来设置区域
_tprintf(TEXT("szStr 的地址: %p lp的地址: %p \n", szStr, lp);
_tprintf(TEXT("szStr = %s lp = %s\n", szStr, lp));
// Unicode 字符集
// szStr 的地址 0014FCC0 lp的地址: 0014FCD6
// szStr = Windows API 是最强大的编程语言 lp = 最为强大的编程语言
// 多字符字符集
// szStr 的地址: 003EFE38 lp的地址 003EFE44
// szStr = Windows API 是最强大的编程语言 lp = 最为强大的编程语言

不管是设置 Unicode 字符集,还是多字节字符集, 都是计算 "Windows API 是"占用了多少字节的问题, 在两种情况下,都能得到正确的结果

lua 复制代码
注意: 用 _tprintf 函数输出中文字符的时候,需要调用 setlocale 函数设置区域为 chs

在一个字符串中查找另一个`字符串``

strstr wcsstr(宽字符)

arduino 复制代码
char *strstr(const char *str // 在这个字符串中搜索
, const char *strSearch) // 要搜索的字符串

wchar_t *wcsstr(const wchar_t *str, const wchar_t *strSearch);

如果 strSearch 是 str 子串,则返回 strSearch 在 str 中首次出现的地址, 如果不是子串,则返回 NULL 通用版本: _tcsstr

从一个字符串中查找另一个字符串中的任何一个字符

strpbrk wcspbrk 通用版本: _tcspbrk

转换字符串大小写

转换成大写: _strupr和_wcsupr 转换成小写: _strlwr和_wcslwr 通用版本: _tcsupr 和 _tcslwr

另外一个字符转换成大写字母的函数是 touppper 和 towupper,通用版本是 _touppper

字符转换成小写字母的函数是 tolower 和 towlower, 通用版本是 _totlower

字符串拼接

strcat wcscat, 通用版本是 _tcscat, 建议使用安全版本的 _tcscat_s

字符串复制

strcpy 和 wcscpy

安全版本是 strcpy_s 和 wcscpy_s

通用版本是 _tcscpy_s

也可以使用 StringCchCopy 函数代替 _tcscopy_s,更加安全,不会出现缓冲区溢出问题

也可以是用内存复制函数 memcpy_s, 该函数可以指定目标缓冲区和原缓冲区的字节数,不会出现缓冲区溢出问题

字符串比较

strcmpy 和 wcscmp

通用版本是 _tcscmp

arduino 复制代码
int strcmp(const char *string1, const char *string2);

int wcscmp(const wchar_t *string1, const wchar_t *string2);

函数对 string1 和 string2 执行序号(ASCII码值)比较并返回一个指示他们关系的值, 返回值指明 string1 和 string2 的大小关系, (比较的时候区分大小写)

分割字符串

strtok,wcstok 和 _tcstok

安全版本是 strtok_s wcstok_s 和 _tcstok_s

函数声明如下

arduino 复制代码
char* strtok_s(
    char * strToken, // 要分割的字符串
    const char *strDelimit, // 分隔符字符串, 分隔符字符串中的每个字符均为分隔符

)

字符串快速排序

进行字符串快速排序的函数是qsort, 安全版本是 qsort_s

arduino 复制代码
void qsort (
    void *base,// 待排序的字符串数组
    size_t num, // 待排序的字符串数组元素的个数
    size_t width, //以字节位单位,各元素占用的空间大小
    int compare(const void *,const void*);//对字符串进行比较的回调函数
)

void qsort_s (
    void *base,// 待排序的字符串数组
    size_t num, // 待排序的字符串数组元素的个数
    size_t width, //以字节位单位,各元素占用的空间大小
    int compare(const void *,const void*);//对字符串进行比较的回调函数
    void *context;; 上面回调函数的参数
)

这个函数对初学者比较复杂,因为涉及回调函数的盖帘,先看示例再做解释吧, 在此以qsort 函数为例

scss 复制代码
LPTSTR arrStr[] = {
    TEXT("架构风格管理.avi"),
    TEXT("模块化内聚.avi"),
    TEXT("总结.aai"),
    TEXT("管理依赖.avi"),
    TEXT("涉及概述.avi"),
    TEXT("分布式.avi")
};
qsort(arrStr, _countof(arrStr),sizeof(LPTSTR), compare);
for (int i =0; i< _countof(arrStr);i++)
    _tprintf(TEXT("%s\n", arrStr[i]));
return 0;

int compare(const voide *arg1, const void *arg2)
{
    // 因为 arg1, arg2 是数组元素的指针,所以需要*(LPTSTR*)
    return _tcscoll(*(LPTSTR *)arg1, *(LPTSTR *)arg2);
}
//输出结果:
分布式.avi
资源管理.avi
管理依赖.avi
合理内聚.avi
涉及概述.avi
总结.avi

qsort函数对指定数组中的元素进行排序,当然,数组元素也可以是其他类型,例如 int 类型, 在进行排序的时候,qsort函数会调用compare函数对两个数组进行比较,这就是回调函数的盖帘,回调函数compare 负责调用,以后还会遇到系统的回调函数

数组元素排序完成后,二分查找一个数组元素就很快了,这就需要使用 bsearch 函数或安全版本 bsearch_s 函数

arduino 复制代码
void *bsearch(
    const void *key , //要查找的数据
    const void *base, // 要从中进行查找的数组
    size_t num, // 被查找数组的
    size_t width, //每个数组元素的长度
    int (*compare)(const void *key, const void *datum);
)

函数使用二分查找法从数组元素 base[0] - base[num-1]中查找参数key指向的数组

字符串与数组类型的相互转换

将字符串转换为多精度浮点型的函数使 atof 和 _wtof,通用版本是 _ttof;

arduino 复制代码
double atof(const char *str);
double _wtof(const wchat_t *str);

将字符串转换成整形或长整型的函数使 atof, _wtoi 或 atol, _wtol, 通用版本是 _ttoi 或_ttol

arduino 复制代码
int atoi(const char *str);
int _wtoi(const wchat_t *str);
long atol(const char *str);
long _wtol(const wchat_t *str);

将字符串转换成64位整形或long long 整形的函数使 _atoi64, _wtoi64 或 atoll _wtoll, 通用版本是 _ttoi64 或 _ttoll

格式化字符串

printf 和 wprintf 函数用于向标准输出设备按指定格式输出信息, 函数声明如下:

arduino 复制代码
int printf(const char *format [, argument]...);
int wprintt(const wchar_t *format [,argument]...);

_tprintf 使 printf 和wprintf 的通用版本,如果定义了 _UNICODE,则_tprint 会被转换成 wprintf, 否则位 printf, 输出中文的时候需要 setlocale(LC_ALL, "chs");

Windows 中的一些字符串函数

lstrlen 计算字符串长度

arduino 复制代码
int WINAPI lstrlen(LPCSTR lpString);
  • lcstcpy 与StringCChCopy 用于字符串复制,不建议使用这个函数,可能会造成缓冲区溢出,缓冲区溢出使应用程序中许多安全问题的根源,在坏的情况下,如果lpstring1 是基于堆栈的缓冲区,则缓冲区溢出会导致攻击者向进程中注入可执行代码

除了新的安全字符串函数, C/C++ 运行库还新增了一些函数,用于在执行字符串处理时提供更多控制

例如 StringCchCopy

Windows 中 ANSI 与 Unicode 版本函数

例如 MessageBox 有 MessageBoxA 和 MessageBoxW 两个版本

arduino 复制代码
int WINAPI MessageBoxA(HWND hwnd, LPCSTR lptext, LPCSTR lpCatption, UINT uType);
int WINAPI MessageBoxW(HWND hwnd, LPCWSTR lptext, LPCWSTR lpCatption, UINT uType);

MessageBxo 实际是一个宏

arduino 复制代码
# ifdef UNICODE
    #define MessageBox MessageBoxW
#else 
    #define MessageBox MessageBoxA

编译器根据是否定义了 UNICODE 来决定时调用哪个版本

ANIS 与 UNICODE 字符串转换

int MultiByteToWideChar{ UNINT CodePage, // 执行转换时使用的代码页 DWORD dwFlags, // 指定转换类型的标志,一般设置为0 LPCSTR lpMultiByteStr, // 指向要转换的多字节字符串指针 int cbMultiByte, // 要转换的多字节字符串大小,以字节为单位 LPWSTR lpWideCharSet, // 指向接受转换以后宽字符的缓冲区的指针 int cchWideChar; // lpWideCharStr 指向的缓冲区大小,以字符为单位 }

ini 复制代码
LPCSTR lpMultiByteStr = "Windows API 程序设计";
// 第一次调用,获取所需缓冲区大小
int nCChWideChar = MultiByteToWideChar(CP_ACP, 0, lpMultiByteStr, -1, NULL ,0);

// 并分配合适大小的缓冲区,进行第二次调用
LPWSTR lpWideCharStr = new WCHAR[nCChWideChar]; // new 是C++中用于动态内存分配的操作符
MUltiByteToWideChar(CP_ACP, 0, lpMultiByteStr, -1, lpWideCharStr,nCchWideStr);
MessageBoxW(NULL, lpWideCharStr, L"Caption", MB_OK);
delete [] lpWideCharStr, // delete 是C++用于释放内存的操作符

WideCharToMultiByte函数医师杨的,只是多了最后两个参数, 用于将宽字符串转换成多字节字符串

相关推荐
小俊俊的博客35 分钟前
海康RGBD相机使用C++和Opencv采集图像记录
c++·opencv·海康·rgbd相机
waicsdn_haha1 小时前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
_WndProc1 小时前
C++ 日志输出
开发语言·c++·算法
薄荷故人_1 小时前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
m0_748240021 小时前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
qq_433554541 小时前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
努力学习编程的伍大侠1 小时前
基础排序算法
数据结构·c++·算法
yuyanjingtao2 小时前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
闻缺陷则喜何志丹2 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie1145141912 小时前
C++ STL CookBook
开发语言·c++·stl·c++20