《Windows核心编程》のWindows返回值

当调用一个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:这是个难以处理的值。返回数量的函数通常返回LONGDWORD,如果由于某种原因,函数无法对想要进行计数的对象进行计数,那么该函数通常返回0-1(根据函数而定)。如果调用的函数返回值为LONGDWORD,那么得阅读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 );

}

我们如何利用该机制来定义自己函数返回的错误代码呢?若要指明函数运行失败,只需要设定线程的最后的错误代码,然后让我们的函数返回FALSEINVALID_HANDLE_VALUENULL或返回任何合适的信息。设定线程的最后错误代码只需调用下面代码:

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或用户定义

你可能感兴趣的:(windows)