我可以使用 .NET SafeHandle 类并确定性地释放其中的句柄吗?

Jim*_*ans 3 c# pinvoke

我有一个 .NET 项目,我在其中从资源中提取,在运行时并基于平台位,包含我需要使用的函数的本机代码 .DLL。我使用 LoadLibrary、GetProcAddress 和 FreeLibrary 来管理我的托管代码中库的加载和使用。

使用完本机库后,我想将其删除。下面是一些伪代码,显示了当前实现的工作流程:

internal class MyClass()
{    
    string nativeLibraryPath = "C:\my\path\to\extracted\library.dll";
    IntPtr nativeLibraryHandle = IntPtr.Zero;

    public void Load()
    {
        nativeLibraryHandle = NativeMethods.LoadLibrary(nativeLibraryPath);
    }

    public void ExecuteFunction()
    {
        IntPtr functionPointer = NativeMethods.GetProcAddress(nativeLibraryHandle, "MyFunctionName");
        // assume MyManagedDelegate is defined as the correct delegate type.
        MyManagedDelegate managedFunction = Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(MyManagedDelegate)) as MyManagedDelegate;
        managedFunction("foo", "bar");
    }

    public void Unload()
    {
        NativeMethods.FreeLibrary(nativeLibraryHandle);
        File.Delete(nativeLibraryPath);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在使用 IntPtr 类型的变量时工作正常。盛行的智慧(最佳实践?)表明 SafeHandle 可能是更好的方法。但是,我的理解是,在使用 SafeHandle 时,不会确定性地调用 ReleaseHandle() 方法。也就是说,在 SafeHandle 上调用 Close() 或 Dispose() 方法只会标记该类进行垃圾回收;在收集对象之前,它不会调用 ReleaseHandle。这对我来说是个问题,因为在卸载之前我无法删除我的本机代码 .DLL。如果我在 ReleaseHandle 期间调用 FreeLibrary,我怎样才能确定性地等待库被释放(不诉诸诸如调用 GC.Collect() 之类的技巧)?

编辑我已经更新了示例以更能代表我的实际代码的结构。请注意,此示例中发布的代码并不完整。我在我的生产代码中包含了必要的错误检查和异常处理。

Han*_*ant 5

不,这不准确。SafeHandle 负责在您的代码忘记释放句柄时释放句柄。只有会发生不确定性,代码,终结器线程上运行。调用 Dispose() 是高度确定的。

作为写你的代码是不是安全的,它会永久泄漏模块句柄时,炸弹上的异常。您应该使用 finally 块来确保释放句柄。这已经足够好了,这里不需要 SafeHandle,因为您将句柄保留为局部变量,并且始终可以确定性地释放它。