- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在与具有以下形式的一些函数的 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 目标:绝对残酷,而且非常容易出错(也失去了自动编码(marshal)处理提供的所有好处)
按类型有一个过载(GetInfoInt
、GetInfoStr
、..):很快就会失控
有一个通用方法使用 GCHandle.Alloc
获取指针并将其传递给基本的 GetInfo
这需要 IntPtr
: 效果很好,但需要对枚举进行特殊处理,因为遗憾的是它们不可 blittable(是的,我知道我可以简单地 GetInfo<[underlying enum type]>
并转换为枚举,但这种做法违背了目的,因为您无法调用通用方法没有反射的运行时确定的类型)和字符串也需要特殊代码
所以我最终认为可能只有三个重载,即 GetInfoBlittable<T>
, GetInfoEnum<T>
, 和 GetInfoStr
将是代码重复和反射巫术之间的最佳折衷。但是,有更好的方法吗?有没有更好的方法来尽可能接近第一个 GetInfo<T>
片段?理想情况下无需切换类型。感谢您的帮助!
FWIW,总的来说,我需要使用 int
来完成这项工作, long
, uint
, ulong
, string
, string[]
, UIntPtr
, UIntPtr[]
、枚举类型、blittable 结构,可能还有 string[][]
.
最佳答案
int
、long
等结构时,您可以使用 Marshal.SizeOf
获取大小和 new IntPtr(&GCHandle.Alloc(...))
..GetEnumUnderlyingType()
获取其中的原始类型,并通过反射和使用获取名为 value__
的字段的值GetValue
枚举对象,您将收到它。我做了一个测试,所以你可以理解:
internal class Program {
public unsafe static int GetInfo(IntPtr t,UIntPtr size) {
if(size.ToUInt32( ) == 4)
Console.WriteLine( *( int* )t.ToPointer( ) );
else //Is it our string?
Console.WriteLine( new string( ( char* )t.ToPointer( ) ) );
return 1;
}
public static unsafe int ManagedGetInfo<T>(T t) {
if (t.GetType().IsEnum) {
var handle = GCHandle.Alloc( t.GetType( ).GetField( "value__" ).GetValue( t ), GCHandleType.Pinned );
var result = GetInfo( handle.AddrOfPinnedObject( ), new UIntPtr( (uint)Marshal.SizeOf( t.GetType().GetEnumUnderlyingType() ) ) );
handle.Free( );
return result;
}
else if (t.GetType().IsValueType) {
var handle = GCHandle.Alloc( t, GCHandleType.Pinned );
var result = GetInfo( handle.AddrOfPinnedObject( ), new UIntPtr( ( uint )Marshal.SizeOf( t ) ) );
handle.Free( );
return result;
}
else if (t is string) {
var str = t as string;
var arr = ( str + "\0" ).ToArray( );
fixed (char *ptr = &arr[0])
{
return GetInfo( new IntPtr( ptr ), new UIntPtr( ( uint )( arr.Length * Marshal.SizeOf( typeof(char) ) ) ) );
}
}
return -1;
}
enum A {
x,y,z
}
private static void Main( ) {
string str = "1234";
int i = 1234;
A a = A.y;
Console.WriteLine( "Should print: " + str );
ManagedGetInfo( str );
Console.WriteLine( "Should print: " + i );
ManagedGetInfo( i );
Console.WriteLine( "Should print: " + ( int )a );
ManagedGetInfo( a );
}
}
哪些输出:
Should print: 1234
1234
Should print: 1234
1234
Should print: 1
1
注意:您需要在项目属性中启用不安全代码才能对其进行测试。
要制作数组,我会给你一些提示:
ValueType
(如 int、long 等)制成数组。您需要做一些类似于字符串的传递方法。string
中创建数组,您需要进行多次分配和一些肮脏的工作。 (代码看起来很原生)关于c# - C# 中的通用 PInvoke,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26882526/
这是我尝试在 C# 中使用的头文件签名和 C 代码: __declspec(dllexport) emxArray_real_T *emxCreateWrapper_real_T(real_T *da
我的项目在 .NET Frame work 3.5 中运行成功且没有错误。但是,当我将它定位到 .NET Frame work 4 时。我得到了错误: “对 PInvoke 函数的调用使堆栈不平衡。这
我在非托管 DLL 中有一个非常简单的函数,但我没有从它获得正确的返回值。 我可以确认通用 PInvoke 机制正在使用我的 C DLL 中的一个函数: /* Return an integer */
使用 PInvoke 时可能会发生哪些异常,或者所有错误均由方法返回值处理,并且由开发人员检查并在需要时引发异常? 最佳答案 对于 P/Invoke,可以肯定地说您需要处理两种类型的错误。 P/Inv
我正在尝试为 modbusm.dll (win-tech.com/html/mbusocx.htm) 创建一个包装器 C# 文件,为此我使用 dumpbin 输出。 文件 modbusm.dll 的转
我正在尝试使用 PInvoke 绑定(bind)此 C 函数。 bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool edi
我目前正在尝试通过 C# pinvoke 使用 SendMessage 从子窗口获取一些文本。但是,我之前对窗口句柄进行硬核化的尝试失败了,因为该值在应用程序启动时发生了变化。有没有办法可靠地获取这个
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
考虑以下 C 函数: void get_lib_version(const char **ver_string); 如何使用 PInvoke 正确编码它?文档说它返回一个指向静态字符串的指针。我认为这
我有个问题,很奇怪。当我在 PInvoke 中使用下一个结构时,它正在工作,但是当我将它更改为类时,它不起作用。 我的 C#: [StructLayout(LayoutKind.Sequential)
我在 C++ 原生 dll 中有以下函数,我想在 C# 应用程序中使用它。 DWORD __cdecl Foo( LPCTSTR Input,
似乎有多种方法可以做到这一点,但我尝试过的示例对我不起作用。 我在某处读到,使用不安全指针是获得需要指针的更复杂结构的方法。 (对不起,我没能再次找到来源) 我最近的尝试看起来像这样: unsafe
我正在尝试在 C++ 中创建一个简单的 dll,并使用 PInvoke 从 C# 中调用它。我想传入一个字节数组,对其进行一些操作,然后发回另一个字节数组。我想我会传入框架和大小,然后创建一个非托管的
我正在尝试使用 P/Invoke 从 C# 调用 C++ 函数。 [DllImport(PATH)] public static extern int PQunescapeByteaWrapper(
我正在尝试使用 GetFileInformationByHandle 函数获取 FILE_ID_BOTH_DIR_INFO。调用后我的所有值都设置为零。 我收到 win32 错误消息: ERROR_B
我有以下 C 代码: const BYTE* Items[3]; Items[0] = item1; Items[1] = item2; Items[2] = item3; int result =
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我有以下 PInvoke:(C 到 C#) [DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)] stat
我正在尝试在 C# 项目中使用 C DLL。 我在 C 中有一个函数: extern __declspec(dllexport) void InitBoard(sPiece board[8][8]);
我如何在 C# 中使用这个 dll 函数?我尝试了以下但出现错误。“外部组件抛出异常。” 我第一次用 C# 和 Delphi 做这个 PInvoke 的东西。 function HTTPGET(loc
我是一名优秀的程序员,十分优秀!