好吧,我承认我是个很懒的人,很多内容是从别人那里复制的。。。不过加了自己的实践,有意外的地方,以后的实验的环境默认为win7x64,Visual Studio 2008,红色的一般是我出意外的地方=_=
主要参考:http://www.cppblog.com/SpringSnow/archive/2009/02/20/74350.html
这一章开篇介绍了windows函数的几种返回值:VOID,BOOL,HANDLE,PVOID,LONG/DWORD。让我们明白,仅仅通过返回值,我们是不能清楚函数调用为什么会失败的。windows内部,函数检测到错误会采用什么机制呢?它是采用“线程本地存储区”的机制来讲相应的错误代码与“主调线程”关联到一起。它可以使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。
错误代码:一个Windows函数返回的错误代码对了解该函数为什么会运行失败常常很有用。Microsoft公司编译了一个所有可能的错误代码的列表(存放在WinError.h头文件中),并且为每个错误代码分配了一个32位的号码;
线程本地存储器:thread-local storage,当一个Windows函数检测到一个错误时,它会使用一个称为线程本地存储器的机制,将相应的错误代码与调用的线程关联起来,这将使线程能够互相独立地运行,而不会影响各自的错误代码;
GetLastError函数:GetLastError函数返回一个32位的错误代码,它主要用于两种情况:1)确定是什么样的错误,此时当Windows函数运行失败时,应该立即调用GetLastError函数,如果调用了另一个Windows函数,它的值很可能被改写;2)搞清楚成功的原因是什么,有些Windows函数之所以能成功运行,其中的原因有很多,比如CreateEvent函数运行成功,既可能是因为你实际创建了该对象,也有可能是因为已经存在带有相同名字的事件内核对象,此时可以调用GetLastError函数可以搞清函数运行成功的原因;
SetLastError函数:SetLastError函数用于设定线程最后的错误代码,这个错误代码既可以是Windows系统自定义的(代码的第29位必须为0),也可以是用户自定义的(代码的第29位必须为1);
FormatMessage函数:FormatMessage函数用于将错误代码转换成它的文本描述;
函数返回的时候,其返回值会指出已发生的一个错误。
我们查看具体是什么错误,在相应的函数执行完成后调用GetLastError()即可。
windows中,错误有三种表示:
一个消息ID(如ERROR_PATH_NOT_FOUND)
消息文本(如the system cannot find the path specified)
一个编号(尽量避免使用)
调试程序的时候,我们可以配置watch窗口,让它始终显示线程的上一个错误代码和错误的文本描述。如
$err,hr
。hr是要显示错误代码的消息文本。
那么我们怎么在自己的程序中显示消息文本呢?文章介绍了利用FormatMessage函数。这里我也介绍一下这个函数的用法:
DWORD FormatMessage(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
DWORD dwLanguageId,
LPTSTR lpBuffer,
DWORD nSize,
va_list* Arguments
);
dwFlags:
# FORMAT_MESSAGE_ALLOCATE_BUFFER // 此函数会分配内存以包含描述字串。
# FORMAT_MESSAGE_FROM_SYSTEM, // 在系统的id映射表中寻找描述字串
# FORMAT_MESSAGE_FROM_HMODULE // 在其他资源模块中寻找描述字串
# FORMAT_MESSAGE_FROM_STRING // 消息ID是个字串,不是个DWORD
#FORMAT_MESSAGE_IGNORE_INSERTS // 允许我们获得含有%占位符的消息,不传递这个标志,就必须在Arguments参数中提供这些占位符的信息
通常为:FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
lpSource:
# 指定了FORMAT_MESSAGE_FROM_HMODULE的话,此参数表示模块的HANDLE
# 指定了FORMAT_MESSAGE_FROM_STRING的话,此参数表示id字串
通常为:NULL
dwMessageId:
消息ID;如果指定FORMAT_MESSAGE_FROM_STRING,将被忽略。
dwLanguageId:
消息描述所用的语言
通常为:0表示自动选择
lpBuffer:
#如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,则为自己提供的缓冲区
#否则为系统LocalAlloc分配,需要被用户LocalFree
nSize:
#如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,则为自己提供的缓冲区大小
#否则为系统LocalAlloc分配之最小缓冲区大小
Arguments:
通常不使用
代码如下:
void ShowError(){//DWORD dwError = GetLastError();DWORD dwError = 5L;HLOCAL hlocal = NULL;
// Use the default system locale since we look for Windows messages.// Note: this MAKELANGID combination has 0 as valueDWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
BOOL fOk = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |FORMAT_MESSAGE_ALLOCATE_BUFFER,NULL, dwError, systemLocale,(PTSTR) &hlocal, 0, NULL);
if (!fOk) {// Is it a network-related error?HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL,DONT_RESOLVE_DLL_REFERENCES);
if (hDll != NULL) {fOk = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |FORMAT_MESSAGE_ALLOCATE_BUFFER,hDll, dwError, systemLocale,(PTSTR) &hlocal, 0, NULL);FreeLibrary(hDll);}}
if (fOk && (hlocal != NULL)){//这个输出可以在VS的“输出窗口 ” 看到// LocalLock锁定一个本地内存项目 并且 返回一个指向内存块第一个Byte的指针OutputDebugString((PCTSTR) LocalLock(hlocal));//这里要把项目属性改为 多字节字符集,不然会出现乱码cout<<(PCTSTR) LocalLock(hlocal)<<endl;LocalFree(hlocal);}}
visual studio 也提供了一个查询错误的小工具,为Error Lookup。通过以上的示例,我们就知道其相应的工作原理。在“工具”里可以找到。