Windows核心编程--错误处理/字符/字符串

来源:互联网 发布:2017流行网络用语 编辑:程序博客网 时间:2024/06/05 23:08

一.错误处理

-Microsoft编辑了一个列表,其中列出了所有可能的错误代码,并为每个错误代码都分配了一个32位的编号。
-在内部,Windows函数检测到错误时,使用“线程本地存储区”,将相应的错误代码和“主调线程”关联到一起。
-查看错误代码:

// 返回由上一个函数调用设置的线程的32位错误代码DWORD GetLastError();

-错误代码信息:
WinError.h头文件包含了Microsoft定义的错误代码列表。
每个错误有三种表示:
消息ID
消息描述
ID具体值
-错误码其它说明:
1.一些Windows函数调用成功可能源于不同原因,借助错误代码可以帮助进一步区分成功的原因。对具有这种行为的函数Platform SDK文档会说明。
2.Windows函数失败后,应马上调用GetLastError,如又调用了另一个Windows函数,此值可能被改写。

-将错误代码转换为相应的文本描述
1.利用Visual Studio的Error Lookup
2.代码

DWORD FormatMessage(    DWORD dwFlags,// 1    LPCVOID pSource,// 2    DWORD dwMessageId,// 3    DWORD dwLanguageId,// 4    PTSTR pszBuffer,// 5    DWORD nSize,// 6    va_list *Arguments// 7);

1.
FORMAT_MESSAGE_ALLOCATE_BUFFER 要求函数分配一块足以容纳错误文本描述的内存
FORMAT_MESSAGE_FROM_SYSTEM 从系统错误码中查找匹配
FORMAT_MESSAGE_FROM_HMODULE 从指定模块错误码中查找匹配
FORMAT_MESSAGE_IGNORE_INSERTS 允许我们获得含%占位符的消息
2.指定FORMAT_MESSAGE_FROM_HMODULE 时是对应的模块句柄
3.错误码
4.0,操作系统默认语言
5.接收缓冲区地址。(缓冲区内存由函数内部分配,后续需要依据此地址进行内存释放。)
6.FORMAT_MESSAGE_ALLOCATE_BUFFER 时,尺寸信息无意义,设为0即可。
7.NULL

-设置自定义的错误代码
Windows函数失败时,设置错误码来指出错误。
Microsoft还允许将这种机制用到我们自己的函数中。
设置线程的上一个错误代码:

VOID SetLastError(DWORD dwErrorCode);

-错误代码分析:

位 31-30 29 28 27-16 15-0 内容 严重性 Microsoft/客户 保留 设备码 异常代码 含义 0,成功1,提示2,警告3,错误 0,Microsoft 1,客户 0 前256个值由Microsoft保留

二.字符和字符串处理

-建议:
1.在应用程序中始终使用Unicode字符串。
2.始终通过新的安全字符串函数来处理这些字符串。
-Unicode字符的UTF-16编码
1.每个字符编码为2个字节。
2.对于16位不足以表示某些语言所有字符的情况,支持使用代理。代理是用32位来表示一个字符的一种方式。

UFT-8,UTF-32
-Unicode字符集和字母表

6位代码 字符 0000-007F ASCII 0080-00FF 西欧语系字母 0100-017F 欧洲拉丁字母 0180-01FF 扩充拉丁字母 0250-02AF 标准音标 02B0-02FF 进格修饰字母 0300-036F 常见的变音符号 0400-04FF 西里尔字母 0530-058F 亚美尼亚文 0590-05FF 希伯来文 0600-06FF 阿拉伯文 0900-097F 梵文字母

-ANSI字符和Unicode字符与字符串数据类型
1.C语言的char表示一个8位ANSI字符。
2.Microsoft的c/c++编译器定义了一个内建的数据类型wchar_t,它表示一个16位的Unicode(UTF-16)字符。
早期的Microsoft编译器没有提供这个内建的数据类型,编译器只在指定了/Zc:wchar_t编译器开关时,才会定义这个数据类型。默认时,在Micorsoft Visual Studio中新建一个c++项目时,这个编译器开关是指定的。
3.声明

char c = 'A';char szBuffer[100] = "A String";wchar_t c = L'A';wchar_t szBuffer[100] = L"A String";

L通知编译器该字符串应该编译为一个Unicode字符串。当编译器将此字符串放入程序的数据段时,会使用UTF-16来编码每个字符。
4.为了与C语言稍微有一点区分,Windows头文件WinNT.h中定义了以下数据类型

typedef char CHAR;typedef wchar_t WCHAR;

5.前缀__nullterminated是一个头部注解,描述了一个类型如何用作函数的参数和返回值。
6.

#ifdef UNICODEtypedef WCHAR TCHAR, *PTCHAR, *PTSTR;typedef CONST WCHAR *PCTSTR;   #define __TEXT(quote) L##quote#elsetypedef CHAR TCHAT, *PTCHAR, *PTSTR;typedef CONST CHAR *PCTSTR;#define __TEXT(quote) quote#endif#define TEXT(quote) __TEXT(quote)

利用这些类型和宏写代码,无论使用ANSI还是Unicode字符,都能通过编译。

-Windosw中的Unicode函数和ANSI函数
1.Windows的一些API函数一般提供Unicode和非Unicode版本。
2.一般Unicode版本做具体实现,非Unicode版本,先把ANSI转换为
Unicode再调用Unicode版本的实现。

#ifdef UNICODE#define CreateWindowEx CreateWindowExW#else #define CreateWindowEx CreateWindowExA#endif

-C运行库中的Unicode函数和ANSI函数
1.C运行库提供了一系列函数来处理ANSI字符和字符串,并提供了另一系列函数来处理Unicode字符与字符串。但,ANSI版本也做具体实现。

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

-C运行库中的安全字符串函数
1.C运行库的一些字符串函数是不安全的。
2.Micorsoft提供了一系列新的函数来取代C运行库的不安全的字符串处理函数。
3.应使用Microsoft在StrSafe.h中定义的新的安全字符串函数。

-初识新的安全字符串函数
1.C运行库现有的字符串处理函数已被标记为废弃不用,如使用,编译器报警告。
2.须在包含其它所有文件后,才包含StrSafe.h.
3.现有的每一个函数(如_tcscpy)都有一个对应的新版本的函数,前面名称相同但最后添加了一个_s后缀。

PTSTR _tcscpy (PTSTR strDestination, PCTSTR strSource);errno_t _tcscpy_s(PTSTR strDestination, size_t numberOfCharacters, PCTSTR strSource);

4.安全字符串函数流程
所有安全字符串函数(_s后缀)的首要任务是验证传给它们的参数值。
要检查的项目包括:
指针不为NULL
整数在有效范围内
枚举值是有效的
缓冲区足以容纳结果数据

如果这些检查中的任何一项失败,函数都会设置局部于线程的C运行时变量errno。并返回一个errno_t值来指出成功或失败。然而,这些函数并不实际返回。而是直接终止应用。

C运行时,实际上允许我们提供自己的函数,在检测到一个无效参数时,会调用此函数。
5.自定义错误处理
5.1.定义一个如下原型的函数

void InvalidParameterHandler(PCTSTR expression,// C运行时实现代码中可能出现的调用失败 PCTSTR function, // 错误函数名PCTSTR file, // 源代码文件unsigned int line, // 源代码行号uintptr_t);

5.2.注册处理程序

_set_invalid_parameter_handler

5.3.禁止由C运行时,触发Debug Assertion Failed对话框
在应用程序开头处,调用

_CrtSetReportMode(_CRT_ASSERT, 0);

在执行了以上三步后,现在在调用String.h中的替代函数时,可以检查返回的errno_t值,只有返回S_OK,才是成功的。其它可能的返回值在errno.h中有定义。
6.举例

TCHAR szBuffer[10] = {TEXT('-'), ..., TEXT('-')};errno_t result = _tcscpy_s(szBuffer, _countof(szBuffer), TEXT("0123456789"));

返回值:ERANGE
szBuffer:首字符设置为’\0’,后续每个字节设置为0xfd。

-在处理字符串时,获得更多控制
1.除了新的字符串函数,C运行库还新增了一些函数,用于在执行字符串处理时提供更多控制。包含在StrSafe.h中。
如,控制填充符,如何截断。

HRESULT StringCchCat(PTSTR pszDest,size_t cchDest,PCTSTR pszSrc,);HRESULT StringCchCatEx(PTSTR szDest,size_t cchDest,PCTSTR pszSrc,PTSTR* ppszDestEnd,size_t *pcchRemaining,DWORD dwFlags);

Cch表示字符数。
还有一系列名称中含Cb的函数,表示用字节数来指定大小。
关于返回值:
S_OK:成功。
STRSAFE_E_INVALID_PARAMETER:失败,将NULL值传给了参数。
STRSAFE_E_INSUFFICIENT_BUFFER:失败,目标缓冲区太小。

不同于_s后缀的字符串函数,当缓冲区太小的时候,这些函数会执行截断。缓冲区太小时,新的函数中,源缓冲区中可以装入目标可写缓冲区的那一部分会被复制,而且最后一个可用字符会被设为’\0’。故前面举例中的结果将变成”012345678”。
2.许多新的函数都有扩展版本。这些版本有三个额外的参数。
size_t * pcchRemaining:
目标缓冲区还有多少字符尚未使用。不考虑终止符。
LPTSTR* ppszDestEnd;
指向终止符位置
DWORD dwFlags;
STRSAFE_FILL_BEHIND_NULL 成功时,dwFlags低字节填充目标缓冲区剩余部分。
STRSAFE_IGNORE_NULLS 把NULL字符串指针视为空字符串
STRSAFE_FILL_ON_FAILURE 失败时,dwFlags低字节填充整个目标缓冲区。目标缓冲区最后一个字节设为’\0’。
STRSAFE_NULL_ON_FAILURE 失败时,首字符设为’\0’
STRSAFE_NO_TRUNCATION 失败时,首字符设为’\0’

-Windows字符串函数
比较字符串

int CompareString(// 语言标识,检查字符在所标识语言中的含义。以符合语言习惯的方式比较LCID locale,// NORM_IGNORECASE 忽略大小写// ...DWORD dwCmdFlags,PCTSTR pString1,// 字符数,如为负数,自动计算长度int cch1,PCTSTR pString2,int cch2)// 得到主调线程语言标识LCID GetThreadLocale();
int CompareStringOrdinal(PCWSTR pString1,int cchCount1,PCWSTR pString2,int cchCount2,BOOL bIgnoreCase)

-建议
1.始终使用安全的字符串处理函数,如后缀为_s的函数,或前缀为StringCch的函数。后者主要在我们想明确控制截断时使用。
2.不要用Kernel32中的字符串处理,如lstrcat。不要用C运行库中不安全的字符串处理函数。
3.比较文件名/路径/XML元素/属性/注册表项登,用CompareStringOrdinal。比较界面显示字符串用CompareString。

-Unicode与ANSI字符串转换

int MultiByteToWideChar(// 标识了与多字节字符串关联的代码页值UINT nCodePage,// 一般传0DWORD dwFlags,// PCSTR pMultiByteStr,// 字节数。-1,自动计算。int cbMultiByte,PWSTR pWideCharStr,// 目标缓冲区最大可用字符数。0,不执行转换,仅返回宽字符数(含终止符'\0')。// 目标缓冲区需要不小于此字符数。int cchWideChar)

1.多字节字符串转换为Unicode步骤
调MultiByteToWideChar,为pWideCharStr传入NULL,为cchWideChar传入0,为cbMultiByte传-1。
分配可容纳转换后Unicode字符串的内存。
调MultiByteToWideChar

int WideCharToMultiByte(UINT uCodePage,DWORD dwFlags,PCWSTR pWideCharStr,// 字符数,-1,自动计算。int cchWideChar,PSTR pMultiByteStr,// 目标缓冲区最大可用字节数。0,不执行转换,仅返回字节数(含终止符'\0')。// 目标缓冲区需要不小于此字符数。int cbMultiByte,// 一个字符在uCodePage指定的代码页中没有对应的表示时,用此做替代符PCSTR pDefaultChar,// 如宽字符串中至少有一个字符不能转换为对应的多字节形式,此变量设为TRUE。否则,FALSE。PBOOL pfUsedDefaultChar)

-导出ANSI和Unicode DLL函数

// .cppBOOL StringReverseW(PWSTR pWideCharStr, DWORD cchLength){    PWSTR pEndOfStr = pWideCharStr + wcsnlen_s(pWideCharStr, cchLength) - 1;    wchar_t cCharT;    while(pWideCharStr < pEndOfStr)    {        cCharT = *pWideCharStr;        *pWideCharStr = *pEndOfStr;        *pEndOfStr = cCharT;        pWideCharStr++;        pEndOfStr--;    }    return TRUE;}BOOL StringReverseA(PSTR pMultiByteStr, DWORD cchLength){       PWSTR pWideCharStr;    int nLenOfWideCharStr;    BOOL fOK = FALSE;    nLenOfWideCharStr = MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, NULL, 0);    pWideCharStr = (PWSTR)HeapAlloc(GetProcessHeap(), 0, nLenOfWideCharStr * sizeof(wchar_t));    if(pWideCharStr == NULL)    {        return fOK;    }    MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, pWideCharStr, nLenOfWideCharStr);    fOK = StringReverseW(pWideCharStr, cchLength);    if(fOK)    {        WideCharToMultiByte(CP_ACP, 0, pWideCharStr, cchLength, pMultiByteStr, (int)strlen(pMultiByteStr), NULL, NULL);    }    HeapFree(GetProcessHeap(), 0, pWideCharStr);    return fOK;}// .hBOOL StringReverseW(PWSTR pWideCharStr, DWORD cchLength);BOOL StringReverseA(PSTR pMultiByteStr, DWORD cchLength);#ifdef UNICODE#define StringReverse StringReverseW#else#define StringReverse StringReverseA#endif

-判断文本文件是ANSI还是Unicode

// IsTextUnicode有助于进行这种分辨// 使用一系列统计性和确定性方法来猜测缓冲区的内容。// 这种方法并不精确,可能返回错误结果// TRUE Unicode// FALSE 非UnicodeBOOL IsTextUnicode(CONST PVOID pvBuffer, int cb, // 字节数// NULL 执行全部测试// 指定了具体的测试项目,函数返回前会设置整数中的相应位,// 以反映每个测试项目的结果PINT pResult);
原创粉丝点击