在此P/Invoke用例中正确使用SafeHandles [英] Correct use of SafeHandles in this P/Invoke use case

查看:75
本文介绍了在此P/Invoke用例中正确使用SafeHandles的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C#中使用不透明的句柄和内部引用计数的本机Dll,我具有以下P/Invoke签名(全部用DllImport属性装饰)

Working in C# with a native Dll, that uses opaque handles and internal reference counting, I have the following P/Invoke signatures (all decorated with DllImport attribute)

[DllImport("somedll.dll"]
public extern IntPtr getHandleOfA(IntPtr handleToB, int index);  //(1)
public extern IntPtr makeNewHandleOfA();                         //(2)
public extern void   addRefHandleToA(IntPtr handleToA);          //(3)
public extern void   releaseHandleToA(IntPtr handleToA);         //(4)
public extern void   doSomethingWithHandle(IntPtr handleToA)     //(5)

这些调用的含义如下:

  1. 从现有的句柄B获取指向不透明类型A的指针/句柄.返回的句柄的内部引用计数不受影响.

  1. Get a pointer/handle to an opaque type A from an existing handle B. The internal reference count of the returned handle is unaffected.

创建一个新的A句柄.内部引用计数已预先增加,该句柄应由具有功能4的客户端释放.否则将发生泄漏.

Create a new handle of A. The internal reference count is pre-incremented, and the handle should be released by the client with function 4, otherwise a leak will occur.

告诉dll在内部增加句柄A的引用计数.这使我们可以确保dll不会在内部释放我们通过功能1获取的句柄.

Tell the dll to internally increase the reference count of a handle A. This allows us to be sure that the dll will not internally release a handle that we have acquired through function 1.

告诉dll以减少句柄的引用计数.如果我们增加了句柄的引用计数,或者是通过函数2获取的,则应调用该函数.

Tell the dll to decrease the ref count of a handle. Should be called if we have increased the ref count of a handle, or acquired it through function 2.

使用手柄执行一些操作

我想用我自己的SafeHandle子类替换IntPtr.当我通过创建新的手柄来获取手柄时,过程很明显.该句柄的ref计数在dll中是预先增加的,因此我只重写SafeHandle的Release函数,然后调用releaseHandleToA(handle).使用这个新类'MySafeHandle',我可以像上面这样更改P/Invvoke签名:

I would like to replace the IntPtr with my own subclass of SafeHandle. When I acquire handles by creating new ones, the procedure is obvious; the handle's ref count is pre-incremented inside the dll, so I just override the Release function of SafeHandle, and call releaseHandleToA(handle). Using this new class 'MySafeHandle', I can change the P/Incvoke signatures above like so:

public extern MySafeHandleA getHandleOfA(MySafeHandleB handleToB, int index);  //(1)
public extern MySafeHandleA makeNewHandleOfA();                                //(2)
public extern void          addRefHandleToA(MySafeHandleA handleToA);          //(3)
public extern void          releaseHandleToA(MySafeHandleA handleToA);         //(4)
public extern void          doSomethingWithHandle(MySafeHandleA handleToA)     //(5)

但是这里有一个错误:在函数1中,获取的句柄没有增加其引用计数,因此尝试释放该句柄将是一个错误.

There is an error here though: in function 1, the handle acquired has not had its refcount increased, so trying to release the handle would be an error.

因此,也许我应该始终确保getHandleOfA调用与立即的addRefHandleToA配对,如下所示:

So, perhaps I should always ensure getHandleOfA calls are paired with an immediate addRefHandleToA, like this:

[DllImport("somedll.dll"]
private extern MySafeHandleA getHandleOfA(MySafeHandleB handleToB, int index);  //(1)
[DllImport("somedll.dll"]
private extern void          addRefHandleToA(MySafeHandleA handleToA);          //(3)

public MySafeHandleA _getHandleOfA(MySafeHandleB handleToB, int index)
{
    var safehandle = getHandleOfA(handleToB, index);
    addRefHandleToA(safeHandle);
    return safeHandle;
}

这样安全吗?

嗯,不,这显然不安全,因为addRefHandleToA(safeHandle);可能会失败.有什么办法可以使它安全吗?

Well, no it is clearly not safe, as addRefHandleToA(safeHandle); could fail. Is there a way I can make it safe?

推荐答案

调用makeNewHandleOfA时,您拥有返回的实例,因此必须释放它. 调用getHandleOfA时,您不拥有返回的实例,但是您仍要管理其生命周期(即:防止基础本机库释放它).

When you call makeNewHandleOfA, you own the returned instance, so you must release it. When you call getHandleOfA, you do not own the returned instance, but you still want to manage its lifecyle (ie: prevent the underlying native library from releasing it).

这意味着您基本上希望针对这两种用例使用不同的发布策略.

It means you basically want different Release strategies for those two use cases.

选项1

使用:

internal class MyOwnedSafeHandleA : MySafeHandleA
{
    protected override bool ReleaseHandle()
    {
        releaseHandleToA(handle);
        return true;
    }
}

internal class MySafeHandleA : SafeHandle
{
    private int refCountIncremented;

    internal void IncrementRefCount(Action<MySafeHandleA> nativeIncrement)
    {
        nativeIncrement(this);
        refCountIncremented++;
    }

    protected override bool ReleaseHandle()
    {
        while (refCountIncremented > 0)
        {
            releaseHandleToA(handle);
            refCountIncremented--;
        }

        return true;
    }
}

您可以这样声明您的DllImports:

You can declare your DllImports like so:

    [DllImport("somedll.dll")]
    public extern MyOwnedSafeHandleA makeNewHandleOfA();
    [DllImport("somedll.dll")]
    private extern MySafeHandleA getHandleOfA(MySafeHandleB handleToB, int index);
    [DllImport("somedll.dll")]
    private extern void addRefHandleToA(MySafeHandleA handleToA);

选项2

您可以这样声明您的SafeHandle:

You could declare your SafeHandle like this:

internal class MySafeHandleA : SafeHandle
{
    MySafeHandleA(IntPtr handle) : base(IntPtr.Zero, true)
    {
        SetHandle(handle);
    }

    protected override bool ReleaseHandle()
    {
        releaseHandleToA(handle);
        return true;
    }
}

并像这样使用它:

[DllImport("somedll.dll"]
private extern IntPtr getHandleOfA(MySafeHandleB handleToB, int index);
[DllImport("somedll.dll"]
private extern void addRefHandleToA(IntPtr ptr);  

public MySafeHandleA _getHandleOfA(MySafeHandleB handleToB, int index)
{
    IntPtr ptr = getHandleOfA(handleToB, index);
    addRefHandleToA(ptr);
    return new MySafeHandleA(ptr);
}

这篇关于在此P/Invoke用例中正确使用SafeHandles的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆