现在的位置: 首页 > 综合 > 正文

Chapter 23: Termination Handlers(2)Understanding Termination Handlers by Example(4)

2018年04月10日 ⁄ 综合 ⁄ 共 5472字 ⁄ 字号 评论关闭
这一部分介绍了:
1、使用SEH简化错误处理
2、使用__leave降低开销

Funcarama1

We've pretty much covered the basic syntax and semantics of termination handlers. Now let's look at how a termination handler could be used to simplify a more complicated programming problem. Let's look at a function that doesn't take
advantage of termination handlers at all:
现在我们看一下termination hander如何被用来简化复杂的编程问题,先来看一下根本不用termination hander的情形:

BOOL Funcarama1() {
   HANDLE hFile = INVALID_HANDLE_VALUE;
   PVOID pvBuf = NULL;
   DWORD dwNumBytesRead;
   BOOL bOk;

   hFile = CreateFile(TEXT("SOMEDATA.DAT"), GENERIC_READ,
      FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
   if (hFile == INVALID_HANDLE_VALUE) {
      return(FALSE);
   }

   pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
   if (pvBuf == NULL) {
      CloseHandle(hFile);
      return(FALSE);
   }

   bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
   if (!bOk || (dwNumBytesRead == 0)) {
      VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
      CloseHandle(hFile);
      return(FALSE);
   }

   // Do some calculation on the data.
   ...
   // Clean up all the resources.
   VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
   CloseHandle(hFile);
   return(TRUE);
}

All the error checking in Funcarama1 makes the function difficult to read, which also makes the function difficult to understand, maintain, and modify.

本例中错误处理会比较难读,并因此产生一系列问题。

Funcarama2

Of course, it's possible to rewrite
Funcarama1
so that it is a little cleaner and easier to understand:
看一下这样写是否更易于理解

BOOL Funcarama2() {
   HANDLE hFile = INVALID_HANDLE_VALUE;
   PVOID pvBuf = NULL;
   DWORD dwNumBytesRead;
   BOOL bOk, bSuccess = FALSE;

   hFile = CreateFile(TEXT("SOMEDATA.DAT"), GENERIC_READ,
      FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

   if (hFile != INVALID_HANDLE_VALUE) {
      pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
      if (pvBuf != NULL) {
         bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
         if (bOk && (dwNumBytesRead != 0)) {
            // Do some calculation on the data.
            ...
            bSuccess = TRUE;
         }
         VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
      }
      CloseHandle(hFile);
   }
   return(bSuccess);
}

Although easier to understand than
Funcarama1
,
Funcarama2
is still difficult to modify and maintain. Also, the indentation level gets to be pretty extreme as more conditional statements are added; with such a rewrite, you soon end up writing code on the far right of your
screen and wrapping statements after every five characters!
与上个版本相比,有改进但仍不足

Funcarama3

Let's rewrite the first version,
Funcarama1
, to take advantage of an SEH termination handler:
使用SEH改写

DWORD Funcarama3() {

   // IMPORTANT: Initialize all variables to assume failure.
   HANDLE hFile = INVALID_HANDLE_VALUE;
   PVOID pvBuf = NULL;

   __try {
      DWORD dwNumBytesRead;
      BOOL bOk;

      hFile = CreateFile(TEXT("SOMEDATA.DAT"), GENERIC_READ,
         FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
      if (hFile == INVALID_HANDLE_VALUE) {
         return(FALSE);
      }

      pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
      if (pvBuf == NULL) {
         return(FALSE);
      }

      bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
      if (!bOk || (dwNumBytesRead != 1024)) {
         return(FALSE);
      }

      // Do some calculation on the data.
      ...
   }

   __finally {
      // Clean up all the resources.
      if (pvBuf != NULL)
         VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
      if (hFile != INVALID_HANDLE_VALUE)
         CloseHandle(hFile);
   }
   // Continue processing.
   return(TRUE);
}

The real virtue of the
Funcarama3
version is that all of the function's cleanup code is localized in one place and one place only: the finally block. If we ever need to
add code to this function, we can simply add a single cleanup line in the finally block—we won't have to go back to every possible location of failure and add our cleanup line to
each failure location.

本例改进的实质在所有的cleanup代码都在finally中,而不必找所有可能出错的地方增加cleanup代码。

Funcarama4: The Final Frontier

The real problem with the
Funcarama3
version is the overhead. As I mentioned after the discussion of Funcenstein4, you really should avoid putting return
statements into a try block as much as possible.

前一个例子的问题是开销。正如前文所述,应该避免在try中放入return

To help make such avoidance easier, Microsoft added another keyword,__leave, to its C/C++ compiler. Here is the Funcarama4
version, which takes advantage of the__leave keyword:

微软在编译器里增加了另一个关键字__leave,以下是示例

DWORD Funcarama4() {

   // IMPORTANT: Initialize all variables to assume failure.
   HANDLE hFile = INVALID_HANDLE_VALUE;
   PVOID pvBuf = NULL;

   // Assume that the function will not execute successfully.
   BOOL bFunctionOk = FALSE;

   __try {
      DWORD dwNumBytesRead;
      BOOL bOk;

      hFile = CreateFile(TEXT("SOMEDATA.DAT"), GENERIC_READ,
         FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
      if (hFile == INVALID_HANDLE_VALUE) {
         __leave;
      }

      pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);

      if (pvBuf == NULL) {
         __leave;
      }

      bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
      if (!bOk || (dwNumBytesRead == 0)) {
         __leave;
      }

      // Do some calculation on the data.
      ...
      // Indicate that the entire function executed successfully.
      bFunctionOk = TRUE;
   }
   __finally {
      // Clean up all the resources.
      if (pvBuf != NULL)
         VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
      if (hFile != INVALID_HANDLE_VALUE)
         CloseHandle(hFile);
   }
   // Continue processing.
   return(bFunctionOk);
}

The use of the __leave keyword in the try block causes a jump to the end of the try
block. You can think of it as jumping to the try block's closing brace. Because the flow of control will exit naturally from the try
block and enter the finally block, no overhead is incurred. However, it was necessary to introduce a new Boolean variable, bFunctionOk,
to indicate the success or failure of the function. That's a relatively small price to pay.

在try中使用__leave使得程序直接跳到try的结尾。因为是自然进入finally,所以没有额外的开销。不过,这里有必要介绍一下新出现的布尔变量bFunctionOk。

When designing your functions to take advantage of termination handlers in this way, remember to initialize all of your resource handles to invalid values before entering your  try
block. Then, in the finally block, you can check to see which resources have been allocated successfully so that you'll know which ones to free. Another popular method for tracking
which resources will need to be freed is to set a flag when a resource allocation is successful. Then the code in thefinally block can examine the state of the flag to determine
whether the resource needs freeing

在使用这种方式的时候,要在进入try之前,把所有的资源句柄置为非法值。这样的话,在finally中,你可以检查哪些资源已经被成功申请,以便你知道哪个该被释放。另一种办法是设置表质量。

抱歉!评论已关闭.