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

时间:2011-01-28 13:51:10

标签: 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);
    }
}

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

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

2 个答案:

答案 0 :(得分:3)

不,那不准确。当您的代码忘记执行此操作时,SafeHandle负责释放句柄。只有 会以非确定的方式发生,在终结器线程上运行的代码。调用Dispose()是高度确定性的。

您编写的代码是安全的,它会在炸弹异常时永久泄漏模块句柄。您应该使用finally块来确保释放句柄。这是非常好的,你不需要SafeHandle,因为你把句柄保存为局部变量,并且总能确定地释放它。

答案 1 :(得分:2)

你错了。
调用Close()将释放句柄。

但是,它只会在引用计数为零时关闭句柄(这由句柄维护; GC不使用引用计数)