【发布时间】:2014-11-12 08:37:45
【问题描述】:
我正在与具有以下形式的一些函数的 C API 交互:
int get_info(/* stuff */, size_t in_size, void* value, size_t *out_size);
这是一个著名的 C 习惯用法,从单个函数返回一堆不同类型的数据:in_size 参数包含传入value 的缓冲区大小,以及实际写入的字节数通过out_size指针写入,调用者可以在value缓冲区中读回它的数据。
我正在尝试从 C# 中很好地调用这些类型的函数(即,无需为每种不同的类型进行重载,而不必单独调用它们,这很丑陋并引入了很多重复的代码)。所以我很自然地尝试了这样的事情:
[DllImport(DllName, EntryPoint = "get_info")]
int GetInfo<T>(/* stuff */, UIntPtr inSize, out T value, out UIntPtr outSize);
令我惊讶的是,它在 Mono 中编译并完美运行。不幸的是,我的希望很快就被 VS 不想编译它所打消,事实上,泛型方法被禁止作为 DllImport 目标。但这基本上是我正在寻找的。我尝试了几件事:
使用反射基于泛型类型动态生成适当的 PInvoke 目标:绝对糟糕,而且非常容易出错(也失去了自动编组提供的所有好处)
按类型有一个重载(
GetInfoInt、GetInfoStr、..):很快就会失控有一个通用方法使用
GCHandle.Alloc获取指针并将其传递给一个基本的GetInfo,它需要一个IntPtr:效果很好,但需要对枚举进行特殊处理,因为遗憾的是它们不是 blittable (是的,我知道我可以简单地GetInfo<[underlying enum type]>并强制转换为枚举,但这违背了目的,因为你不能调用具有运行时确定类型的泛型方法而不需要反射)并且字符串也需要特殊代码
所以我最终认为可能恰好有三个重载,GetInfoBlittable<T>、GetInfoEnum<T> 和 GetInfoStr 将是代码重复和反射巫术之间的最佳折衷。但是,有没有更好的方法?有没有更好的方法来尽可能接近第一个GetInfo<T> sn-p?理想情况下不需要切换类型。感谢您的帮助!
FWIW,总的来说,我需要使用int、long、uint、ulong、string、string[]、UIntPtr、UIntPtr[]、枚举类型来完成这项工作, blittable 结构,可能还有string[][]。
【问题讨论】:
-
显然,框架本身并不知道如何将某些字节转换为所有可以通用提供的类型。