如何构造一个 try-catch-finally 块来处理 finally 中的错误?
How can I structure a try-catch-finally block to handle errors inside finally?
我在调用第三方 C++ dll 时遇到问题,我使用 DllImport
将其包装在 class 中以访问其函数。
dll 要求在使用之前打开一个会话,returns 一个整数句柄用于在执行操作时引用该会话。完成后,必须使用同一句柄关闭会话。所以我做了这样的事情:
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: One or more errors occurred. --->
System.AccessViolationException: Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
我不确定我能做些什么来阻止这个错误,因为它似乎是由以线程方式使用 Dll 引起的——它应该是线程安全的。但是由于我的 CloseSession() 函数除了调用 Dll 的关闭函数外什么都不做,所以我没有太多回旋余地 "fix" 任何事情。
然而,最终结果是会话没有正确关闭。因此,当该进程再次尝试时(它应该这样做),它会遇到一个打开的会话并不断抛出新的错误。该会话绝对 必须 关闭。
我不知道如何设计更健壮的错误处理语句来确保会话始终关闭?
我会更改包装器以包括处理外部资源并包装句柄。 IE。您可以用包装器对象来表示会话,而不是用句柄来表示会话。
此外,将对 DLL 的调用包装在 lock
语句中(如 @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()`.
您可以通过调用此 class Session
以及方法 Open
和 Close
来改进命名。这个 class 的用户不需要知道它是一个包装器。这只是一个实现细节。此外,方法的命名现在是对称的,无需重复名称 Session
.
通过封装所有与会话相关的内容,包括错误处理、错误情况恢复和资源处置,您可以大大减少代码中的混乱。 Session
class 现在是一个高级抽象。旧的DllWrapper
介于低级和高级之间。
我在调用第三方 C++ dll 时遇到问题,我使用 DllImport
将其包装在 class 中以访问其函数。
dll 要求在使用之前打开一个会话,returns 一个整数句柄用于在执行操作时引用该会话。完成后,必须使用同一句柄关闭会话。所以我做了这样的事情:
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: One or more errors occurred. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
我不确定我能做些什么来阻止这个错误,因为它似乎是由以线程方式使用 Dll 引起的——它应该是线程安全的。但是由于我的 CloseSession() 函数除了调用 Dll 的关闭函数外什么都不做,所以我没有太多回旋余地 "fix" 任何事情。
然而,最终结果是会话没有正确关闭。因此,当该进程再次尝试时(它应该这样做),它会遇到一个打开的会话并不断抛出新的错误。该会话绝对 必须 关闭。
我不知道如何设计更健壮的错误处理语句来确保会话始终关闭?
我会更改包装器以包括处理外部资源并包装句柄。 IE。您可以用包装器对象来表示会话,而不是用句柄来表示会话。
此外,将对 DLL 的调用包装在 lock
语句中(如 @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()`.
您可以通过调用此 class Session
以及方法 Open
和 Close
来改进命名。这个 class 的用户不需要知道它是一个包装器。这只是一个实现细节。此外,方法的命名现在是对称的,无需重复名称 Session
.
通过封装所有与会话相关的内容,包括错误处理、错误情况恢复和资源处置,您可以大大减少代码中的混乱。 Session
class 现在是一个高级抽象。旧的DllWrapper
介于低级和高级之间。