《Windows via C/C++》学习笔记(二):Working with Characters and String

1. 字符编码与数据类型

编码

字节数

类型

字符(串)常量

WinNT.h中的定义

ANSI

8bit

char

'A'

"A string"

typedef char CHAR;

typedef CHAR *PCHAR;

typedef CHAR *PSTR;

typedef CONST CHAR *PCSTR;

Unicode (UTF-16)

16bit

wchar_t

通过编译器设置/Zc:wchar_t支持

L'A'

L"A string"

typedef char CHAR;

typedef CHAR *PWCHAR;

typedef CHAR *PWSTR;

typedef CONST CHAR *PCWSTR;

WinNT.h头文件中定义字符类型宏,TCHAR、PTCHAR、PTSTR、PCTSTR、TEXT(),可在ANSI和Unicode编码间通用。

2. Windows函数中关于字符编码的信息

  • 使用UNICODE宏区分程序的编码是使用ANSI或Unicode;
  • 在Vista中ANSI编码的函数在内部首先将ANSI转化为Unicode再调用Unicode版本去做;
  • 为兼容16位的旧Windows函数只有ANSI编码版本,应被替换掉,如OpenFile -> CreateFile, WinExec -> CreateProcess等;
  • 新的Windows函数只有Unicode版本,如ReadDirectoryChangesW和CreateProcessWithLogonW;
  • COM接口要求使用Unicode;
  • 资源文件通常为Unicode编码,Vista下系统可自动转换编码。

3. C运行时库中关于字符编码的信息

  • C运行时库提供Unicode、ANSI两个版本,但两者不会相互调用;
  • 使用_UNICODE宏区分Unicode和ANSI编码。

4. C运行时库的安全字符函数

潜在隐患:目标字符缓冲区未能承载结果字符,导致内存崩溃。

解决方法:包含StrSafe.h头文件

a. 使用_tcscpy_s、_tcscat_s代替_tcscpy、_tcscat,其函数原型为

errno_t _tcscpy_s(PTSTR strDestination, size_t numberofCharacters, PCSTR strSource);

errno_t _tcscat_s(PTSTR strDestination, size_t numberofCharacters, PCSTR strSource);

  • 其中参数numberofCharacters使用_countof宏(在stdlib.h中定义)计算字符的数目去确定缓存大小;
  • 安全函数返回errno_t,但并非真正返回。在Debug模式下编译的程序会弹出一断言对话框,Release模式下编译的程序会直接退出。

错误处理方法:

① 定义一错误处理函数,函数原型如下:

void InvalidParameterHandler(PCSTR expression, PCSTR function, PCSTR file, unsigned in line, uintptr_t /*pReserved*/);

② 使用_set_invalid_parameter_handler注册处理函数;

③ 程序开始时,调用_CrtSetReportMode(_CRT_ASSERT, 0)关闭断言对话框。

  • 当字符串经过安全函数处理时出现错误后,字符串首字符变为'\0',缓冲区的其余部分由0xfd填充。

b. 更多对字符处理的控制

包括以字符数目计算缓冲区大小的函数,函数名中带有Cch(count of characters)

StringCchCat(Ex),StringCchCopy(Ex),StringCchPrintf(Ex)

和以字节数计算缓冲区大小的函数,函数名中带有Cb(count of bytes)

StringCbCat(Ex),StringCbCopy(Ex),StringCbPrintf(Ex)

  • 函数返回值类型为HRESULT

S_OK:表示字符处理成功;

STRSAFE_E_INVALID_PARAMETER:非法参数,如pszDest为NULL、错误的dwFlags等;

STRSAFE_E_INSUFFICIENT_BUFFER

:目标字符串缓冲区空间不足,但目标字符串会尽可能放入字符并以'\0'结束字符串。

  • 以Ex结尾的字符串处理函数提供了更多的控制,增加了三个参数:

LPTSTR* ppszDestEnd:返回目的字符串缓冲区结束字符('\0')的字符指针;

size_t* pcchRemaining:返回目的字符串缓冲区未使用字符数;

DWORD dwFlags:关于字符处理的标记。

数值

描述

STRSAFE_FILL_BEHIND_NULL

当函数运行成功时,将dwFlags低字节填充到目的字符串未初始化的字符上。

STRSAFE_IGNORE_NULLS

将NULL字符串指针视为(TEXT(""))。

STRSAFE_FILL_ON_FAILURE

当函数运行失败时,将dwFlags低字节填充到整个目的字符串上,除了第一个字符设置为'\0'。

STRSAFE_NULL_ON_FAILURE

当函数运行失败时,将目的字符串的第一个字符设置为'\0',并将字符串视为(TEXT(""))。

STRSAFE_NO_TRUNCATION

目的字符串被设置为(TEXT(""))。

c. Windows字符函数关于字符串比较的函数

可使用CompareString(Ex)和CompareStringOrdinal比较字符串:

  • CompareString(Ex)是根据系统的区域设置,从语言学角度去比较两个字符串。函数需要获得一32位的区域设置编号(LCID),可通过GetThreadLocale函数获得。
  • CompareStringOrdinal则是针对程序代码中的字符串比较,只做码点比较(code-point comparison)。该函数只有Unicode编码的版本。
  • 返回值为int。可将返回值减2,即可得到与C运行时库中字符串比较函数相同的返回值。

数值

描述

 

0

字符串比较操作失败。

CSTR_LESS_THAN

1

pString1小于pString2。

CSTR_EQUAL

2

字符串相等。

CSTR_GREATER_THAN

3

pString1大于pString2。

5. 使用Unicode的理由

  • 便于程序的全球化和本地化;
  • 可发布独立的支持所有语言的二进制文件;
  • 提高程序运行的效率和减少内存占用(部分Windows函数会在内部将ANSI字符串转为Unicode再继续运行);
  • 某些Windows函数只提供Unicode编码版本;
  • 可确保程序能够与COM整合;
  • 可确保程序能够与.Net Framework整合;
  • 便于程序资源操作(资源中的字符串通常为Unicode编码)。

6. 使用字符及字符串的推荐方法

  • 以字母数组的角度,而非char数组和字节数组的角度去看待字符串;
  • 对于文本字符和字符串,使用通用的数据类型,如TCHAR/PTSTR;
  • 对于字节,字节指针和数据缓冲区,使用明确的数据类型,如BYTE/PBYTE;
  • 对于原义字符及字符串使用TEXT()或_T()宏,但不要混合使用;
  • 全球化,如使用PTSTR代替PSTR;
  • 使用_countof()宏计算字符个数,多用于确定缓冲区大小。使用_sizeof()计算字节数,多用于分配内存空间;
  • 避免printf系列函数,特别是使用%s和%S去实现ANSI和Unicode相互转换,应使用MultiByteToWideChar和WideCharToMultiByte;
  • 总是同时使用或不使用UNICODE和_UNICODE宏。

7. 操作字符串的推荐方法

  • 总是使用安全函数(以_s为后缀或以StringCch为前缀的函数);
  • 不要使用不安全的C运行时库函数,即没有以目标字符串大小为函数参数的函数。

对于缓冲区的操作,C运行时库提供了相应的更新版本,例如memcpy_s,memmove_s,wmemcpy_s,wmemmove_s。使用这些函数,需要声明__STDC_WANT_SECURE_LIB__宏和包含CrtDefs.h;

  • 使用编译器设置/GS和/RTCs;
  • 不使用Kernel32中的方法,例如lstrcat和lstrcpy;
  • 程序代码中使用的字符串的比较使用CompareStringOrdinal,用户界面上的字符串比较使用CompareString(Ex)。

8. Unicode与ANSI的相互转化

  • 使用MultiByteToWideChar,将多字节字符串转化为宽字符串,函数原型为:

int MultiByteToWideChar(UINT uCodePage, DWORD dwFlag, PCSTR pMultiByteStr, int cbMultiByte, PWSTR pWideCharStr, int cchWideChar)

转换步骤:

① 以NULL为pWideCharStr的值、0为cchWideChar的值、-1为cbMultiByte的值调用MultiByteToWideChar,由返回值得到源字符串的字符个数;

② 分配内存,大小为源字符串的字符个数×sizeof(wchar_t);

③ 再次调用MultiByteToWideChar,将目的字符串缓冲区地址、字符个数等参数传入;

④ 使用字符;

⑤ 释放字符。

  • 使用WideCharToMultiByte,将宽字符串转化为多字节字符串,函数原型为:

int WideCharToMultiByte(UINT uCodePage, DWORD dwFlag, PCWSTR pWideCharStr, int cchWideChar, PSTR pMultiByteStr, int cbMultiByte, PCSTR pDefaultChar, PBOOL pfUsedDefaultChar)

转换步骤:

与MultiByteToWideChar类似。参数pDef

aultChar作为默认字符,用于替代未能在多字节字符中找到对应字符的宽字符,pfUsedDefaultChar用于返回是否有宽字符未能找到对应的多字节字符。

  • 通过MultiByteToWideChar和WideCharToMultiByte函数,在实现时只实现Unicode编码版本的函数。通过内部调用MultiByteToWideChar转换再调用Unicode编码版本的方式,实现ANSI编码版本的函数。最后使用宏的方式统一函数命名。

在CPP文件中:

void FuncW(PWSTR pWideCharStr, DWORD cchLength)

{

......

}

void FuncA(PSTR pMultiByteStr, DWORD cchLength)

{

PWSTR pWideCharStr;

int nLenOfWideCharStr;

nLenOfWideCharStr = MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, NULL, 0);

pWideCharStr = (PWSTR) HeapAlloc(GetProcessHeap(), 0, nLenOfWideCharStr * sizeof(wchar_t));

If (pWideCharStr == NULL)

return;

MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, pWideCharStr, nLenOfWideCharStr);

FuncW(pWideCharStr, cchLength);

WideCharToMultiByte(CP_ACP, 0, pWideCharStr, cchLength, pMultiByteStr, (int)strlen(pMultiByteStr), NULL, NULL);

HeapFree(GetProcessHeap(), 0, pWideCharStr);

return;

}

在头文件中:

void FuncW(PWSTR pWideCharStr, DWORD cchLength);

void FuncA(PSTR pMultiByteStr, DWORD cchLength);

#ifdef UNICODE

#define Func FuncW

#else

#define Func FuncA

#endif
  • 区分Unicode与ANSI编码

使用AdvApi32.dll中,在WinBase.h中定义的IsTextUnicode函数去区分文件使用的是ANSI还是Unicode编码。该函数并未能精确得出文件的编码。其函数原型为:

BOOL IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);

原文地址:https://www.cnblogs.com/alonecat06/p/2710658.html