在传递给非托管代码之前将委托固定在结构中

2024-04-09

我正在尝试使用非托管 C dll 将图像数据加载到 C# 应用程序中。该库有一个相当简单的接口,您可以在其中传递一个包含三个回调的结构,一个用于接收图像的大小,一个用于接收每一行像素,最后一个在加载完成时调用。像这样(C# 托管定义):

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct st_ImageProtocol
{
   public st_ImageProtocol_done Done;    
   public st_ImageProtocol_setSize SetSize;    
   public st_ImageProtocol_sendLine SendLine;
}

启动 stb_Image 协议的类型是委托:

public delegate int st_ImageProtocol_sendLine(System.IntPtr localData, int rowNumber, System.IntPtr pixelData);

在我使用的测试文件中,SetSize 应该被调用一次,然后 SendLine 将被调用 200 次(图像中的每行像素一次),最后触发 Done 回调。实际发生的情况是,SendLine 被调用 19 次,然后抛出 AccessViolationException,声称该库试图访问受保护的内存。

我可以访问 C 库的代码(尽管我无法更改功能),并且在调用 SendLine 方法的循环期间,它不会分配或释放任何新内存,所以我的假设是委托本身是问题,我需要在传递它之前固定它(目前我在委托本身内部没有代码,除了一个计数器来查看它被调用的频率,所以我怀疑我是否破坏了托管端的任何内容)。问题是我不知道该怎么做;我一直用来在非托管空间中声明结构的方法不适用于委托(Marshal.AllocHGlobal()),并且我找不到任何其他合适的方法。委托本身是 Program 类中的静态字段,因此它们不应该被垃圾收集,但我猜运行时可能会移动它们。

Chris Brumme 的这篇博客文章 http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx表示在传递给非托管代码之前不需要固定委托:

显然,非托管函数指针必须引用固定地址。如果 GC 重新安置它,那将是一场灾难!这导致许多应用程序为委托创建固定句柄。这是完全没有必要的。非托管函数指针实际上指的是我们动态生成以执行转换和封送的本机代码存根。该存根存在于 GC 堆之外的固定内存中。

但我不知道当委托是结构的一部分时这是否成立。它确实意味着可以手动固定它们,而且我对如何执行此操作或关于为什么循环运行 19 次然后突然失败的任何更好建议感兴趣。

Thanks.


编辑回答约翰的问题......

分配struct的代码如下:

_sendLineFunc = new st_ImageProtocol_sendLine(protocolSendLineStub);

_imageProtocol = new st_ImageProtocol()
                     {
                          //Set some other properties...
                          SendLine = _sendLineFunc
                     };

int protocolSize = Marshal.SizeOf(_imageProtocol);
_imageProtocolPtr = Marshal.AllocHGlobal(protocolSize);
Marshal.StructureToPtr(_imageProtocol, _imageProtocolPtr, true);

其中 _sendLineFunc 和 _imageProtocol 变量都是 Program 类的静态字段。如果我正确理解了它的内部原理,那就意味着我将一个非托管指针传递给copy_imageProtocol 变量复制到 C 库中,但该副本包含对静态 _sendLineFunc 的引用。这应该意味着副本不会被 GC 触及 - 因为它是非托管的 - 并且委托不会被收集,因为它仍然在范围内(静态)。

该结构实际​​上作为另一个回调的返回值传递到库,但作为指针:

private static IntPtr beginCallback(IntPtr localData, en_ImageType imageType)
{
    return _imageProtocolPtr;
}

基本上还有另一种结构类型保存图像文件名和指向此回调的函数指针,库确定文件中存储的图像类型,并使用此回调请求给定类型的正确协议结构。我的文件名结构的声明和管理方式与上面的协议相同,因此可能包含相同的错误,但由于该委托仅被调用一次并被快速调用,所以我还没有遇到任何问题。


编辑更新

感谢大家的回复,但在这个问题上又花了几天时间但没有取得任何进展后,我决定搁置它。如果有人感兴趣,我正在尝试为 Lightwave 3D 渲染应用程序的用户编写一个工具,一个很好的功能是能够查看 Lightwave 支持的所有不同图像格式(其中一些相当奇特)。我认为最好的方法是为 Lightwave 用于图像处理的插件架构编写一个 C# 包装器,这样我就可以使用他们的代码来实际加载文件。不幸的是,在针对我的解决方案尝试了许多插件之后,我遇到了各种我无法理解或修复的错误,我的猜测是 Lightwave 没有以标准方式调用插件上的方法,可能是为了提高安全性运行外部代码(我承认是在黑暗中进行的疯狂刺杀)。目前我将放弃图像功能,如果我决定恢复它,我将以不同的方式处理它。

再次感谢,尽管我没有得到我想要的结果,但通过这个过程我学到了很多东西。


我在注册回调委托时遇到了类似的问题(它会被调用,然后噗!)。我的问题是具有被委托方法的对象正在被 GC 处理。我在一个更全局的地方创建了该对象,以防止它被 GC 回收。

如果这样的方法不起作用,还需要注意以下一些其他事项:

作为附加信息,请查看获取委托函数指针 http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getfunctionpointerfordelegate.aspx来自元帅级。这是您可以做到这一点的另一种方式。只需确保代表未被 GC 处理即可。然后,将它们声明为 IntPtr,而不是结构中的委托。

这可能无法解决固定问题,但请看一下fixed http://msdn.microsoft.com/en-us/library/f58wzh21(VS.80).aspx关键字,即使这可能对您不起作用,因为您处理的生命周期比通常使用的生命周期更长。

最后,看看stackalloc http://msdn.microsoft.com/en-us/library/cx9s2sy4(VS.80).aspx用于创建非 GC 内存。这些方法将需要使用unsafe,因此可能会对您的程序集施加一些其他限制。

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

在传递给非托管代码之前将委托固定在结构中 的相关文章

  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • char指针或char变量的默认值是什么[重复]

    这个问题在这里已经有答案了 下面是我尝试打印 char 变量和指针的默认值 值的代码 但无法在控制台上看到它 它是否有默认值或只是无法读取 ASCII 范围 include
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐