当调用一个Windows函数时,它首先检验传递给它的各个参数的有效性,然后再设法执行任务。如果传递了一个无效参数,或者由于某种原因无法执行这项操作,那么操作系统就会返回一个值,指明该函数在某种程度上运行失败了。
下面说说Windows常见的返回值类型:
1) VOID:表明该函数运行不可能失败,Windows函数的返回值很少是VOID。
2) BOOL:如果函数运行失败,返回值是0,否则返回值是非0值。最好对返回值进行测试,以确定它是0还是非0,而不是测试返回值是否是TRUE。
3) HANDLE:如果函数运行失败,则返回值通常是NULL,否则返回值是某个HANDLE,用于标识你可以操作的一个对象。F.Y.I,有些函数失败时会返回一个句柄值INVALID_HANDLE_VALUE,它被定义为-1。函数的Platform SDK文档将会清楚地说明函数运行失败时返回值是NULL还是INVALID_HANDLE_VALUE。
4) PVOID:如果函数运行失败,返回值是NULL,否则返回PVOID,以标识数据块的内存地址。
5) LONG/DWORD:这是个难以处理的值。返回数量的函数通常返回LONG或DWORD,如果由于某种原因,函数无法对想要进行计数的对象进行计数,那么该函数通常返回0或-1(根据函数而定)。如果调用的函数返回值为LONG或DWORD,那么得阅读Platform SDK以确保能正确检测潜在的错误。
一个Windows函数返回的错误代码对了解函数运行失败的原因是很有用的。Microsoft编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。若要确定这是个什么错误,可以调用GetLastError函数:
DWORD GetLastError();
该函数只返回线程的32位错误代码,我们需要将它转换成更便于理解的某种东西。在WinError.h头文件中包含了Microsoft定义的错误代码的列表。格式如下:
// DNS_INFO_NO_RECORDS 0x0000251d
//
// MessageId: DNS_INFO_NO_RECORDS
//
// MessageText:
//
// No records found for given DNS query.
//
#define DNS_INFO_NO_RECORDS 9501L //(0x0000251d的十进制值)
我们可以看到,每个错误有三种表示法:一个消息ID(这是我们可以在源代码中使用的一个宏,以便于与GetLastError的返回值进行比较)、消息文本(对错误的英文描述)和一个号码(应该避免直接使用该号码,而是使用消息ID)
当Windows函数运行失败时,应该立即调用GetLastError函数。因为如果调用另一个Windows函数,GetLastError的返回值很可能被改写。
如果在编写的应用程序中发现一个错误,可能需要向用户显示该错误的文本描述。Windows提供了一个函数,可以将错误代码转换成它的文本描述。该函数就是FormatMessage:
DWORD FormatMessage(
__in DWORD dwFlags, // source and processing options
__in_opt LPCVOID lpSource, // pointer to message source
__in DWORD dwMessageId, // requested message identifier
__in DWORD dwLanguageId, // language identifier for requested message
__out LPTSTR lpBuffer, // pointer to message buffer
__in DWORD nSize, // maximum size of message buffer
__in_opt va_list *Arguments // pointer to array of message inserts
);
FormatMessage函数的功能是非常丰富的,在创建向用户显示的字符串信息时,它是首选函数。原因之一是它很容易用多种语言进行操作,该函数能够检测出用户首选地语言,并返回相应的文本。
下面是FormatMessage的实例代码:
#include <windows.h>
#include <lmerr.h>
#include <tchar.h>
#define ERRMSGBUFFERSIZE 256
void fnDisplayError( DWORD dwErrorMsgId )
{
DWORD ret; // Temp space to hold a return value.
HINSTANCE hInst; // Instance handle for DLL.
HLOCAL pBuffer; // Buffer to hold the textual error description.
if ( HRESULT_FACILITY(dwErrorMsgId) == FACILITY_MSMQ )
{
// MSMQ errors only (see winerror.h for facility info).
// Load the MSMQ library containing the error message strings.
hInst = LoadLibrary( TEXT("MQUTIL.DLL") );
if(hInst != 0)
{
// hInst not NULL if the library was successfully loaded.
// Get the text string for a message definition
ret = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | // Function will handle memory allocation.
FORMAT_MESSAGE_FROM_HMODULE | // Using a module's message table.
FORMAT_MESSAGE_IGNORE_INSERTS,
hInst, // Handle to the DLL.
dwErrorMsgId, // Message identifier.
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
(LPTSTR)&pBuffer, // Buffer that will hold the text string.
ERRMSGBUFFERSIZE, // Allocate at least this many chars for pBuffer.
NULL // No insert values.
);
} // hInst not NULL if the library was successfully loaded.
} // MSMQ errors only.
else if ( dwErrorMsgId >= NERR_BASE && dwErrorMsgId <= MAX_NERR )
{
// Could be a network error.
// Load the library containing network messages.
hInst = LoadLibrary( TEXT("NETMSG.DLL") );
if(hInst != 0)
{
// Not NULL if successfully loaded.
// Get a text string for the message definition.
ret = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | // The function will allocate memory for the message.
FORMAT_MESSAGE_FROM_HMODULE | // Message definition is in a module.
FORMAT_MESSAGE_IGNORE_INSERTS, // No inserts used.
hInst, // Handle to the module containing the definition.
dwErrorMsgId, // Message identifier.
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
(LPTSTR)&pBuffer, // Buffer to hold the text string.
ERRMSGBUFFERSIZE, // Smallest size that will be allocated for pBuffer.
NULL // No inserts.
);
} // Not NULL if successfully loaded.
} // Could be a network error.
else
{
// Unknown message source.
// Get the message string from the system.
ret = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | // The function will allocate space for pBuffer.
FORMAT_MESSAGE_FROM_SYSTEM | // System wide message.
FORMAT_MESSAGE_IGNORE_INSERTS, // No inserts.
NULL, // Message is not in a module.
dwErrorMsgId, // Message identifier.
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
(LPTSTR)&pBuffer, // Buffer to hold the text string.
ERRMSGBUFFERSIZE, // The function will allocate at least this much for pBuffer.
NULL // No inserts.
);
}
// Display the string.
if( ret )
{
_tprintf( _TEXT("/tERRORMESSAGE: %s/n"), (LPTSTR)pBuffer );
}
else
{
_tprintf( _TEXT("/tERRORNUMBER: %d/n"), dwErrorMsgId );
}
// Free the buffer.
LocalFree( pBuffer );
}
我们如何利用该机制来定义自己函数返回的错误代码呢?若要指明函数运行失败,只需要设定线程的最后的错误代码,然后让我们的函数返回FALSE、INVALID_HANDLE_VALUE、NULL或返回任何合适的信息。设定线程的最后错误代码只需调用下面代码:
VOID SetLastError(DWORD dwErrCode);
可以将合适的任何32位号码传递给该函数,包括WinError.h中已经存在的代码,只要该代码能够正确指明想要报告的错误即可。我们也可以创建自己的错误代码。错误代码是一个32位的数字,格式如下表:
位 |
31~30 |
29 |
28 |
27~16 |
15~0 |
内容 含义 |
严重性 0=成功 1=供参考 2=警告 3=错误 |
0=Microsoft定义的代码
1=客户定义的代码 |
保留 必须是0 |
设备代码 由Microsoft定义 |
异常代码 由Microsoft或用户定义 |