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

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(使用前将#替换为@)

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

  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • C#动态支持吗?

    看完之后这个帖子 https stackoverflow com questions 2674906 when should one use dynamic keyword in c sharp 4 0k和链接 我还有 2 个问题 问题 1
  • 以编程方式检查页面是否需要基于 web.config 设置进行身份验证

    我想知道是否有一种方法可以检查页面是否需要基于 web config 设置进行身份验证 基本上如果有这样的节点
  • 对齐 GridView 中的行值

    我需要在 asp net 3 5 中右对齐 gridview 列中的值 我怎样才能做到这一点
  • POCO HTTPSClientSession 发送请求时遇到问题 - 证书验证失败

    我正在尝试使用 POCO 库编写一个向服务器发出 HTTPS 请求的程序 出于测试目的 我正在连接到具有自签名证书的服务器 并且我希望允许客户端进行连接 为了允许这种情况发生 我尝试安装InvalidCertificateHandler这是
  • ASP MVC:服务应该返回 IQueryable 的吗?

    你怎么认为 你的 DAO 应该返回一个 IQueryable 以便在你的控制器中使用它吗 不 您的控制器根本不应该处理任何复杂的逻辑 保持苗条身材 模型 而不是 DAO 应该将控制器返回给视图所需的所有内容 我认为在控制器类中看到查询 甚至
  • C 语言中 =+(等于加)是什么意思?

    我碰到 与标准相反 今天在一些 C 代码中 我不太确定这里发生了什么 我在文档中也找不到它 In ancientC 版本 相当于 它的残余物与最早的恐龙骨头一起被发现 例如 B 引入了广义赋值运算符 使用x y to add y to x
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • 通过 NHibernate 进行查询,无需 N+1 - 包含示例

    我有一个 N 1 问题 我不知道如何解决它 可以在这个问题的底部找到完全可重复的样本 因此 如果您愿意 请创建数据库 设置 NUnit 测试和所有附带的类 并尝试在本地消除 N 1 这是我遇到的真实问题的匿名版本 众所周知 这段代码对于帮助
  • 当“int”处于最大值并使用 postfix ++ 进行测试时,代码定义良好吗?

    示例 未定义行为的一个示例是整数溢出的行为 C11dr 3 4 3 3 int溢出是未定义的行为 但这是否适用于存在循环的以下内容 并且不使用现在超出范围的副作用i 特别是 这是否后缀增量规格帮助 结果的值计算在副作用之前排序 更新操作数的
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 耐用功能是否适合大量活动?

    我有一个场景 需要计算 500k 活动 都是小算盘 由于限制 我只能同时计算 30 个 想象一下下面的简单示例 FunctionName Crawl public static async Task
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化
  • 结构体指针的动态数组

    我必须使用以下代码块来完成学校作业 严格不进行任何修改 typedef struct char firstName char lastName int id float mark pStudentRecord pStudentRecord
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 双精度类型二维多维数组的 pinvoke 编组作为 c# 和 c++ 之间的输入和输出

    我有以下我正在尝试解决的双物质类型的 2d 多维数组的 c 和 c pinvoke 编组 我已经查看了以下热门内容以获得我目前拥有的内容使用双精度数组进行 P Invoke 在 C 和 C 之间编组数据 https stackoverflo
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检

随机推荐