最近在学习关于windows操作系统的一些知识。首先想拿来与大家分享的就是关于windows error handling这块。之后我看时间安排,会写一些关于windows其他的方面的随笔,与大家分享学习结果。
我们现在遇到的许多操作系统,其实会有很多我们所想象到和意料之外的一些功能(feature)。但我想最主要的,在了解这些东西之前,我们或许应该先了解一些windows底层如何去展现这些功能(feature)在error这块上面的处理。最基本的,我们会想到的是,如果我们去调用一个windows方法,它会去验证你所传入这个方法的参数是否合法。我列举一下我们比较常用的windows方法的返回类型。
1. VOID
这类方法不可能失败。不过也很少一些windows方法返回这样一个类型。
2. BOOL
这类方法如果失败,会返回数值0;否则,返回一个非0的数值。当然我们可以理解它要么是TRUE要么就是FALSE。在WinDef.h里面,你会发现它的定义是:
typedef int BOOL;
3。 HANDLE
这类方法如果失败,会返回NULL;否则,返回一个你可以使用的对象标识。
typedef PVOID HANDLE;
这里需要注意的,如果它返回INVALID_HANDLE_VALUE,会返回-1。在Platform SDK文档里面定义这个类型返回为NULL或者INVALID_HANDLE_VALUE(失败)。
4. PVOID
这类方法如果失败,会返回NULL;否则,返回一个具体的内存中的地址。
5. LONG/DWORD
这两类方法的返回会根据具体的方法返回不一样。一般来说它们会返回LONG/DWORD,当然有些情况造成失败,通常会返回0或者-1。具体的话,大家可以参考:http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx 这里面罗列的比较详细。
以上是我罗列了一些比较常用的windows方法返回类型。那么当windows方法返回一个错误码(error code),这样一个错误码,常常是需要被使用者很好的理解。所以微软本身提供了一些错误码的列表,这些错误码的格式是一个32位格式。一般windows方法检测一个错误,通常是采用thread-local storage的机制去关联合适的错误码。关于thread-local storage我之后时间我会单独谈一下。那么当windows方法返回后,我们怎么具体去查看一个比较确切的错误。一般来说,我们可以使用GetLastError方法。他的定义,我们在Winbase.h文件里可以查看到,我们发现这个方法只是简单的返回最后一个调用方法所产生的32位错误码。
#ifdef _M_CEE_PURE #define GetLastError System::Runtime::InteropServices::Marshal::GetLastWin32Error #else WINBASEAPI __checkReturn DWORD WINAPI GetLastError( VOID ); #endif
当然简单的一个32位错误码,我们可能不能理解它的含义,所以我们需要把它转换成以一定的形式来显示,这样对于我们使用者来说会更有意义。我们去查看Winerror.h头文件的时候,我们会发现很多微软定义的错误码。比如:
// // MessageId: ERROR_FILE_NOT_FOUND // // MessageText: // // The system cannot find the file specified. // #define ERROR_FILE_NOT_FOUND 2L
我们会发现一般来说,他包含3部分:MessageId,MessageText和一个具体的数值。MessageId显而易见,可以提供我们在编程的时候进行返回值的比对。而MessageText对对于这样一个错误码的描述。那么在编程的时候我们可以调用GetLastError方法,在debug的时候呢?我们可以在Watch Window(我电脑上的VS是英文版,所以不是很确定这个中文应该怎么翻译,或许应该叫查看窗口?),然后键入ERR, hr,去显示错误码。具体大家可以参考:http://msdn.microsoft.com/en-us/library/aa232400(v=vs.60).aspx
那么我们自己去检测一个错误码的时候,我们应该怎么去把这个错误码的具体描述显示给我们终端用户呢?Windows提供了一个叫FormatMessage方法,这个方法的签名是:
DWORD FormatMessage( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize, va_list* Arguments );
其实使用这个方法我们可以写我们自己的错误显示/提示的小工具。当然这个过程中我们会用到一些windows方法,但是都是比较简单。比如GetDlgItemInt, MAKELANGID(这里我们是去设置它使用默认的系统地区信息,因为我们FormatMessage方法其中有个参数是和语言有关dwLanguageId), SetDlgItemText等。当然网上会有许多这类错误码查看工具,微软其实本身也有,像Err小工具。
最后我们来看下微软在错误码这块怎么给我们用户预留扩展的接口的。首先,我们可以很方法的使用SetLastError方法去设置我们的错误码,它的方法签名:
void SetLastError(DWORD dwErrCode);
另外我们这里就需要考虑一个自定义错误码的问题。其实一个错误码分为以下几个部分:(因为一个错误码是32位,我下面以第几位到第几位作说明)
31-30:
这里有几种情况。0:Success;1=Informational;2=Warning;3=Error
29:
这里就是我们自定义错误码的重点。0:Microsoft-defined code;1:customer-defined code
28:
这个位置必须为0,这个位置不能是其实值。
27-16:
这里一共12个位置,所以一共会有4096种可能的值,而前256个值微软已经使用了。所以我们自定义的错误码只能使用后面一些位置。
15-0:
最后这块位置是一个异常码。
大概要讲的就是这些,准备的有些仓促,有错误的地方,望各位大虾指点。