在Windows编程中我们调用各种Windows API函数来使用系统提供的各种基础支持,这些API函数并不是总是都能够预期完成所规定的工作的,有些时候会因为各种原因导致函数调用失败,比如传给函数的参数不正确,调用函数的权限不够,请求的资源系统暂时分配不了等等。
大多时候根据函数的返回值,可以判断函数是否失败
Windows除了使用返回值标识函数执行情况,外还引入了错误代码机制。返回值指出函数已发生一个错误。要查看具体是什么错误,就应该使用错误代码。如当我们调用函数打开一个文件,如果打开失败,可能出现多种原因。一是:文件不存在,另一种是该文件被其他程序互斥使用。在这两种情况下都导致文件打开失败,返回值仅仅标识打开失败,但我们不知道原因。这时错误代码就派上用场了。
Windows将错误代码存储于独立于线程的,线程局部存储区中。顾名思义,这个区域是每个线程独有的其他线程无法更改。它存储着一些线程独有的东西,在第二十一章有详细的介绍。在函数执行后,就可以通过调用GetLastError()来获得错误代码。因此这个错误代码永远反映的是上一个函数的执行情况,也就是说它的值是由上一个函数设置的。因此,当调用一个API函数后应该在调用其他API之前使用GetLastError'来获取具体的错误代码(避免被其他API改写Error值),进而判定失败的原因。
有些时候从API函数的返回值来判定为成功,但是函数的成功可能缘于不同的原因,例如使用CreateEvent创建一个具名的事件对象时,函数成存在两种情况:1、对象实际创建成功2、打开了已存在的同名事件对象。这时我们可以调用GetLastError根据返回值是不是ERROR_ALREADY_EXISTS来判定是不是打开了同名的对象。
GetLastError函数的原型如下
DWORD WINAPI GetLastError(void);
他的返回值是一个DWORD类型,在Windows SDK的winerror.h文件中为这些表示错误的DWORD值定义了宏和注释,格式如下
//
// MessageId: ERROR_SUCCESS
//
// MessageText:
//
// The operation completed successfully.
//
#define ERROR_SUCCESS 0L
#define NO_ERROR 0L // dderror
#define SEC_E_OK ((HRESULT)0x00000000L)
//
// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION 1L // dderror
//
// MessageId: ERROR_FILE_NOT_FOUND
//
// MessageText:
//
// The system cannot find the file specified.
//
#define ERROR_FILE_NOT_FOUND 2L
//
// MessageId: ERROR_PATH_NOT_FOUND
//
// MessageText:
//
// The system cannot find the path specified.
//
#define ERROR_PATH_NOT_FOUND 3L
//
// MessageId: ERROR_TOO_MANY_OPEN_FILES
//
// MessageText:
//
// The system cannot open the file.
第一行是错误代码的宏定义 每一个DWORD对应一个宏标记,方面理解错误代码的函数,这些宏都以ERROR_开头,宏下面的第三行是对错误代码的含义注释
在我们自己编写的函数中可以使用这种机制,我们的函数可能因为N中原因导致失败,我们可以再让函数返回失败的值得同时为每一种失败原因设置同Windows AP一样的错误码,Windows提供了让我们来设置错误码的系统函数 SetLastError,
void WINAPI SetLastError(
__in DWORD dwErrCode
);
我们应该尽量使用WinError.h中现有的错误码,只要可以很好的反应想报告的错我。如果没有合适的可以定义自己错误码,32位的错误码被分为了不同的字段,其结构如下
可以定义自己的错误代码宏,来方便的完成错误的定义
例如 我们定义一个错误码表示在一个获取网页的函数中域名不正确,可以这样来做
#define FACILITY_HTTP 257#define COVERT_HTTP(x) (x<=0?x:((x&0x0000FFFF)|((FACILITY_HTTP<<16)|0xA0000000)))
另外Windwos提供了一个API函数FormatMessage函数来获取相应错误码的 详细描述 具体使用参见MSDN
DWORD WINAPI FormatMessage(
__in DWORD dwFlags,
__in_opt LPCVOID lpSource,
__in DWORD dwMessageId,
__in DWORD dwLanguageId,
__out LPTSTR lpBuffer,
__in DWORD nSize,
__in_opt va_list *Arguments
);
QT实现了下Error Lookup程序
#include "dialog.h"
#include "ui_dialog.h"
#include
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->edit_ErrorCode->setValidator(new QIntValidator(0,65536,this));
hlocal = (LPSTR)VirtualAlloc(NULL,256,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_btn_LookUp_pressed()
{
QString str = ui->edit_ErrorCode->text();
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL);
BOOL bRet = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS \
| FORMAT_MESSAGE_MAX_WIDTH_MASK,NULL,str.toLong(),0,(LPSTR)hlocal,256,NULL);
if(!bRet)
{
HMODULE hDll = LoadLibraryExA("netmsg.dll",NULL,DONT_RESOLVE_DLL_REFERENCES);
if(hDll != NULL)
{
bRet = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS \
| FORMAT_MESSAGE_ALLOCATE_BUFFER,hDll,str.toLong(),systemLocale,hlocal,256,NULL);
FreeLibrary(hDll);
}
}
QString strMsg;
if(!bRet)
{
memset(hlocal,0,256);
sprintf(hlocal,"无法找到关于%d的描述",str.toLong());
strMsg = QString::fromUtf8(hlocal);
}
else
{
strMsg = QString::fromLocal8Bit(hlocal);
}
ui->textEdit->clear();
ui->textEdit->append(strMsg);
memset(hlocal,0,256);
}