第1章 错误处理

第1章 错误处理

标签: 函数设计 返回类型 错误处理


本章内容
1. 1 定义自己的错误代码
1. 2 ErrorShow示例程序

调用Windows函数时,它会先验证我们传给它的参数,然后再开始执行任务。如果传入的参数无效,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数因为某些原因失败了。

表1-1 常见的Windows函数返回值数据类型

数据类型指出函数调用失败的值
VOID这个函数不可能失败。只有极少数Windows函数的返回值类型为VOID
BOOL这个函数失败,返回值为0;否则,返回值是一个非0值。应避免测试返回值是否为TRUE;最稳妥的做法是检查它是否不为FALSE
HANDLE如果函数失败,则返回值通常为NULL;否则,HANDLE将标识一个可以操纵的对象。请注意这种返回值,因为某些函数会返回为INVALID_HANDLE_VALUE的一个句柄值,它被定义为-1。函数的Platform SDK文档清楚说明了函数是返回NULL还是INVALID_HANDLE_VALUE来标识失败
PVOID如果函数调用失败,则返回值为NULL;否则,PVOID将标识一个数据块的内存地址
LONG/DWORD这种类型比较棘手。返回计数的函数通常会返回一个LONGDWORD。如果函数处于某种原因不能对我们想要计数的东西进行比较,它通常会返回0或-1(具体取决于函数)。如果要调用一个返回LONG/DWORD的函数,务必仔细阅读Platform SDK文档,确保我们会正确地检查可能出现的错误


谈谈自己的体会:
比如以下代码:

VOID swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

很简单,利用指针实现了两个int类型数据的交换。但是,代码中存在严重的漏洞,如果传入的指针中有NULL值出现,怎么办?显然不符合我们的预期,我们理应暂停交换,然后告知调用者问题的根源。
然后,我们开始更改代码:

BOOL swap(int *a, int *b)
{
    if (a == NULL || b == NULL)
    {
        return FALSE;
    }

    int temp = *a;
    *a = *b;
    *b = temp;
}

好了,这段代码已经看起来像那么回事了,但是它还不完美。当传入NULL值时,我们只是知道它失败返回了FALSE,并不知道它到底因为什么原因失败的。


在内部,当Windows函数检测到错误时,它会使用一种名为“线程本地存储区”的机制将相应的错误代码与“主调线程”关联到一起。这种机制使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。函数返回时,其返回值会指出已发生一个错误,要查看具体是什么错误,请调用GetLastError函数,如下所示:

DWORD GetLastError();

此函数的作用很简单,就是返回由上一个函数调用设置的线程的32位错误代码。
有了32位错误代码之后,接着需要把它转换为更有用的信息。WinError.h头文件包含了Microsoft定义的错误代码列表。

//
// MessageId: ERROR_SUCCESS
//
// MessageText:
//
//  The operation completed successfully.
//
#define ERROR_SUCCESS                    0L

#define NO_ERROR 0L                         // dderror

//
// 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.
//
#define ERROR_TOO_MANY_OPEN_FILES        4L

//
// MessageId: ERROR_ACCESS_DENIED
//
// MessageText:
//
//  Access is denied.
//
#define ERROR_ACCESS_DENIED              5L

可以看出,每个错误都有三种表示:一个消息ID、消息文本和一个编号。

GetLastError()可以获取错误代码。当然了,如果想生成向用户显示的字符串,FormatMessage是首选的方式。

1.1 定义自己的错误代码
Microsoft还允许我们在设计供其他人调用的函数时,向调用者指出错误。

int swap(int *a, int *b)
{
    if (a == NULL || b == NULL)
    {
        SetLastError(RPC_X_NULL_REF_POINTER);
        return FALSE;
    }

    int temp = *a;
    *a = *b;
    *b = temp;

    SetLastError(ERROR_SUCCESS);
    return TRUE;

}


int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) 
{

   int *a = NULL;
   int b = 3;

   int result = swap(a, &b);
   DWORD dwErrorId = GetLastError();

   return(0);
}

SetLastError

上图可以明显看出,错误代码是0x000006f4(1780L),多亏有了hr限定符,Watch窗口进一步指出错误代码1780L是“向占位程序传递了空的索引指针”。


1.2 ErrorShow示例程序

ErrorShow应用程序(01-ErrorShow.exe)演示了如何得到一个错误代码的文本描述。此应用程序的源代码和资源文件可以在本书配套网页的01-ErrorShow目录中找到,网址为http://www.wintellect.com/devcenter/category/resources

程序主要利用了FormatMessage生成了错误描述字符串,感兴趣的同学可以自行下载代码阅读。此处我贴下关键代码和演示结果。

      // Get the error code
      DWORD dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);

      HLOCAL hlocal = NULL;   // Buffer that gets the error message string

      // Use the default system locale since we look for Windows messages.
      // Note: this MAKELANGID combination has 0 as value
      DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);           // Chinese
      //DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);      // English
      // Get the error code's textual description
      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)) {
         SetDlgItemText(hwnd, IDC_ERRORTEXT, (PCTSTR) LocalLock(hlocal));
         LocalFree(hlocal);
      } else {
         SetDlgItemText(hwnd, IDC_ERRORTEXT, 
            TEXT("No text found for this error number."));
      }

演示结果




小结
1. 第1章主要讲了GetLastError获取错误代码,FormatMessage用来生成错误描述字符串。
2. 新年开始上班,准备从Windows核心编程读书笔记开始,专心写写博客,希望自己能坚持下来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值