2017-04-18 49 views
0

我在使用DllImport访问它的函数时打了一个第三方C++ dll的调用问题,这个dll是我用类包装的。我怎样才能构建一个try-catch-finally块来最终处理错误?

该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的问题抛出Error运行线程时:

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

我不知道我能做些什么来阻止这个错误,因为它似乎是通过以线程方式使用Dll而产生的 - 它应该是线程安全的。但是由于我的CloseSession()函数除了调用该Dll的关闭函数外什么都不做,因此我没有太多摆动空间来“修复”任何东西。

但最终结果是会话无法正常关闭。所以当这个过程再次尝试时,它会遇到一个开放的会话,并不断抛出新的错误。该会议绝对被关闭。

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

+4

我想问题是,错误应该如何“处理”?如果关闭会话会从第三方代码中引发错误,并且我们无法继续关闭会话,那么下一步是什么?我的意思是,你可以在'finally'里放置另一个'try/catch',但是你会如何回应这个错误?如果第三方工具不起作用,可以做些什么? – David

+4

如果这是由线程引起的,那么也许可以通过在StartSession周围使用[lock(){}](https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx)语句来解决此问题()和CloseSession()方法调用。 – Serge

+0

@David第三方工具失败是间歇性的 - 当你第二次尝试时,它通常起作用。我的实际代码目前是一个非常糟糕的递归和控制流程,试图确保始终发生。我很讨厌添加更多,但如果必须,我必须:( –

回答

1

我会改变包装来包括处理外部资源,也包装句柄。即而不是通过句柄来表示一个会话,你可以用一个包装器对象来表示它。

此外,将调用包装到lock -statements(如@Serge建议)中,可以完全防止多线程问题。请注意,锁对象是静态的,所以所有的DllWrapper都使用同一个锁对象。

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位于低级和高级之间的中间位置。

+0

谢谢你。我非常喜欢将它作为解决问题并同时整理代码的手段。这种方法的初始原型似乎已经减慢了很多处理速度,我不知道为什么。我正在调查 - 如果我能解决问题,我会很乐意接受。 –