我遇到了调用第三方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的关闭函数之外什么都不做,因此我没有太多的余地去“修复”任何东西。
然而,最终结果是会话未正确关闭。因此,当进程再次尝试时,它应该执行,它会遇到一个打开的会话,并且不断抛出新的错误。该会话绝对已关闭。
我不知道如何设计一个更强大的错误处理语句,以确保会话始终关闭?
答案 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
以及方法Open
和Close
来改进命名。该类的用户不需要知道它是一个包装器。这只是一个实现细节。此外,方法的命名现在是对称的,无需重复名称Session
。
通过封装所有与会话相关的内容,包括错误处理,从错误情况中恢复以及处理资源,您可以大大减少代码中的混乱。 Session
类现在是一个高级抽象。旧DllWrapper
位于低级别和高级别之间的中间位置。