gpt4 book ai didi

c# - 在此 P/Invoke 用例中正确使用 SafeHandles

转载 作者:太空宇宙 更新时间:2023-11-03 11:09:42 30 4
gpt4 key购买 nike

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

[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 的指针/句柄。返回句柄的内部引用计数不受影响。

  2. 新建A的句柄,内部引用计数预增,句柄需要客户端用函数4释放,否则会发生泄漏。

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

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

  5. 用句柄进行一些操作

我想用我自己的 SafeHandle 子类替换 IntPtr。当我通过创建新句柄来获取句柄时,过程很明显; handle 的引用计数在 dll 中预先递增,所以我只是重写了 SafeHandle 的 Release 函数,并调用 releaseHandleToA(handle)。使用这个新类“MySafeHandle”,我可以像这样更改上面的 P/Incvoke 签名:

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 中,获取的句柄没有增加其引用计数,因此尝试释放句柄将是一个错误。

所以,也许我应该始终确保 getHandleOfA 调用与即时 addRefHandleToA 配对,如下所示:

[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);可能会失败。有什么办法可以让它安全吗?

最佳答案

当您调用makeNewHandleOfA 时,您拥有 返回的实例,因此您必须释放它。当您调用 getHandleOfA 时,您不拥有返回的实例,但您仍然希望管理它的生命周期(即:防止底层本地库释放它)。

这意味着您基本上需要针对这两个用例使用不同的发布策略。

选项 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:

    [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:

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);
}

关于c# - 在此 P/Invoke 用例中正确使用 SafeHandles,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14524720/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com