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

2023-12-19

在 C# 中使用本机 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。当我通过创建新句柄来获取句柄时,过程很明显;句柄的引用计数在 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, you own返回的实例,因此您必须释放它。 你打电话时getHandleOfA, you 不拥有返回的实例,但您仍然希望管理其生命周期(即:防止底层本机库释放它)。

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

Option 1

With:

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

Option 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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在此 P/Invoke 用例中正确使用 SafeHandles 的相关文章

  • 有没有办法将所有内容都包含在 dbcontext 中?

    当查询一个DbContext急切加载时 需要Include Navigation 为了填充导航属性 然而 在某些情况下 我想简单地Include all实体的导航属性 有没有办法做到这一点 或者有办法做到这一点 我假设你可以反思 但我宁愿避
  • strtok() 和空字段

    我正在将一些 C 结构序列化为字符串 然后将其反序列化strtok 但不幸的是 strtok 不检测空字段 例如 1 2 4 有没有替代功能 在linux上有strsep http www mkssoftware com docs man3
  • fork 和 exec 之间的区别

    两者有什么区别fork and exec 指某东西的用途fork and exec它体现了 UNIX 的精神 它提供了一种非常简单的方法来启动新进程 The fork调用基本上复制了当前进程 在almost任何方式 并非所有内容都会被复制
  • C++ 标准是否允许未初始化的 bool 导致程序崩溃?

    我知道一个 未定义的行为 C 几乎可以让编译器做任何它想做的事情 然而 我遇到了一次令我惊讶的崩溃 因为我认为代码足够安全 在这种情况下 真正的问题仅发生在使用特定编译器的特定平台上 并且仅在启用优化的情况下发生 我尝试了几种方法来重现问题
  • 以概率从列表中选择随机元素

    我有一个包含四个项目 A B C D 的列表 每个项目都有被选择的概率 例如 A 有 74 的机会被选中 B 15 C 7 D 4 我想创建一个函数 根据其概率随机选择一个项目 有什么帮助吗 为您的项目定义一个类 如下所示 class It
  • 如何获取 C# PriorityQueue 元素的优先级

    我正在初始化一个存储 XY 坐标的优先级队列 根据距原点的欧几里得距离确定优先级 我创建了一个自定义Comparer这使得它作为最大堆运行 PriorityQueue
  • 有没有办法关闭 Hangfire 使用 Serilog 进行的日志记录?

    有没有办法关闭 Hangfire 使用 Serilog 进行的日志记录 我们正在使用我们自己的抽象 我不希望在使用 Serilog 时来自 Hangfire 记录器的所有额外噪音 INIT call under web project na
  • WCF 客户端返回空数组 - XML 响应似乎正常

    我正在尝试为我们的 Intranet 上托管的 Web 服务创建一个简单的 WCF 客户端 C 使用 Fiddler 和 SoapUI 我可以看到请求和响应似乎正常 但是当我运行代码时返回一个空数组 我会尝试只粘贴相关的行 但会是很多东西
  • 泛型类上的 DebuggerDisplay

    我在应用时遇到问题DebuggerDisplay泛型类的属性 DebuggerDisplay foo class Foo DebuggerDisplay Bar t class Bar
  • 如何在C中递归地找到另一个字符串中的字符串位置?

    我们有一个任务来创建带有两个字符串参数的递归函数 原型应该是这样的 int instring char word char sentence 如果我们愿意调用函数 instring Word Another Word 它应该具有以下返回值
  • 使用 INF 文件 C++ 以编程方式安装驱动程序

    这里有人可以告诉我如何安装第 3 方设备驱动程序吗 如果提供了所有必需的文件 即 inf 文件 sys 等 则以编程方式进行 这 该解决方案应运行的最低操作系统是Windows2000 我尝试复制 inf文件放入Win文件夹 INF文件夹和
  • C语言中的array、&array、&array[0]有什么区别? [复制]

    这个问题在这里已经有答案了 在学习C语言中的数组和指针时 我很困惑 为什么ch ch ch 0 彼此相等 而sptr sptr sptr 0 却不相等 这是我的源代码 int main void char ch 7 1 2 3 4 5 6
  • 具有多重继承的不明确基数

    我正在尝试在一个大库中编写一些类的子类 我收到 基础不明确 错误 这是该问题的一个可编译示例 include
  • 访问结构向量

    我有一个结构 struct OutputStore int myINT string mySTRING 如果我创建一个 OutputStore 类型的数组 如下所示 OutputStore OutputFileData new Output
  • C++头文件问题

    我在处理类时尝试了一些 C 代码 这个问题出现在我身上 并且让我有点烦恼 我创建了一个包含类定义的头文件和一个包含实现的 cpp 文件 如果我在不同的 cpp 文件中使用此类 为什么要包含头文件而不是包含类实现的 cpp 文件 如果我包含类
  • 我们可以使用 C# 录制发送到扬声器的声音吗

    我有一个软件 SoundTap Streaming Audio Recorder 它记录发送到扬声器的任何音频 无论流是来自网络还是来自某些文件或麦克风 我可以在桌面应用程序中制作这样的应用程序 以便我可以录制发送到扬声器的流 无论来源如何
  • Lambda 按值捕获和“mutable”关键字

    关键词的必要性mutable在 lambda 中 是造成极大混乱的根源 考虑代码 int x 10 function
  • int 类型的构造函数

    考虑到成本 这些情况是否相同 case 1 int a 5 case 2 int a 5 case 3 int a a 5 这三种语法是不同的 请耐心等待 我使用用户定义类型而不是 int 稍后我将回到 int T a 5 Direct i
  • 使用 /clr 或 clr:pure(cpprestsdk 又名 casablanca)编译时不支持互斥

    我创建一个CLR project in visual c with 64 bit配置 并尝试使用cpprestsdk aka casablanca 64bit 但是当我运行项目时 出现了错误 1 gt Build started Proje
  • 返回右值 - 这段代码有什么问题? [复制]

    这个问题在这里已经有答案了 我遇到了以下代码片段 std string test std string m Hello return std move m int main std string m test 我知道上面的代码是不正确且不安

随机推荐