如何构建一个try-catch-finally块来最终处理内部错误?

时间:2017-04-18 13:54:31

标签: c# multithreading dll error-handling unmanaged

我遇到了调用第三方C ++ dll的问题,我使用DllImport将其包含在一个类中以访问其函数。

dll要求在使用之前打开一个会话,该会话返回一个整数句柄,用于在执行操作时引用该会话。完成后,必须使用相同的句柄关闭会话。所以我做了这样的事情:

public void DoWork(string input)
{
    int apiHandle = DllWrapper.StartSession();

    try
    {
        // do work using the apiHandle
    }
    catch(ApplicationException ex)
    {
        // log the error
    }
    finally
    {
        DllWrapper.CloseSession(apiHandle);
    }
}

我遇到的问题是,CloseSession()有时会导致有问题的Dll在运行线程时抛出错误:

  

System.AggregateException:发生了一个或多个错误。 --->   System.AccessViolationException:尝试读取或写入受保护的   记忆。这通常表明其他内存已损坏。

我不确定我能做些什么来阻止这个错误,因为它似乎是以线程方式使用Dll引起的 - 它应该是线程安全的。但是由于我的CloseSession()函数除了调用Dll的关闭函数之外什么都不做,因此我没有太多的余地去“修复”任何东西。

然而,最终结果是会话未正确关闭。因此,当进程再次尝试时,它应该执行,它会遇到一个打开的会话,并且不断抛出新的错误。该会话绝对关闭。

我不知道如何设计一个更强大的错误处理语句,以确保会话始终关闭?

1 个答案:

答案 0 :(得分:1)

我会更改包装器以包含外部资源的处理并包装句柄。即而不是通过句柄表示会话,您可以通过包装器对象来表示它。

此外,在lock语句中包装对DLL的调用(如@Serge建议的那样)可以完全阻止多线程问题。请注意,锁定对象是静态的,因此所有DllWrappers都使用相同的锁定对象。

public class DllWrapper : IDisposable
{
     private static object _lockObject = new object();

     private int _apiHandle;
     private bool _isOpen;

     public void StartSession()
     {
         lock (_lockObject) {
             _apiHandle = ...; // TODO: open the session
         }
         _isOpen = true;
     }

     public void CloseSession()
     {
         const int MaxTries = 10;

         for (int i = 0; _isOpen && i < MaxTries; i++) {
             try {
                 lock (_lockObject) {
                     // TODO: close the session
                 }
                 _isOpen = false;
             } catch {
             }
         }
     }

     public void Dispose()
     {
         CloseSession();
     }
}

请注意,这些方法现在是实例方法。

现在,您可以使用using语句确保关闭会话:

using (var session = new DllWrapper()) {
    try {
        session.StartSession();
        // TODO: work with the session
    } catch(ApplicationException ex) {
        // TODO: log the error
        // This is for exceptions not related to closing the session. If such exceptions
        // cannot occur, you can drop the try-catch completely.
    }       
} // Closes the session automatically by calling `Dispose()`.

您可以通过调用此类Session以及方法OpenClose来改进命名。该类的用户不需要知道它是一个包装器。这只是一个实现细节。此外,方法的命名现在是对称的,无需重复名称Session

通过封装所有与会话相关的内容,包括错误处理,从错误情况中恢复以及处理资源,您可以大大减少代码中的混乱。 Session类现在是一个高级抽象。旧DllWrapper位于低级别和高级别之间的中间位置。