我正在尝试使用用 ANSI C(编译为 C++)编写的第三方 DLL。其中一个函数具有以下 C++ 原型(prototype):
tResultCode GetAttrib
(
tHandle handle,
tAttrib attrib,
void * value,
tAttribType valueType,
int valueMaxSize
);
其中GetAttrib
的参数如下:
handle
是指向 DLL 中不透明结构的空指针attrib
表示从结构中检索哪个属性value
指向由valueType
指示的类型的变量的指针
valueType
指示分配用于保存缓冲区的 C++ 类型 属性值valueMaxSize
是分配给value
指向的变量的字节数
eAttribType
枚举定义如下:
typedef enum eAttribType
{
eHandle,
eBool,
eEnum,
eInt,
eLong,
eFloat,
eDouble,
eDate,
eTime,
eTimestamp,
eString, // char *
eChar, // char
eVoid,
eHandle,
eFunctionPointer
} tAttribType;
eAttrib
枚举定义如下:
typedef enum eAttrib
{
eInvoiceNumber, // eString
eInvoiceDate, // eTimestamp
eUnits, // eLong
ePrice, // eFloat
eDiscount, // eDouble
ePreferredFlag, // eBool
...
} tAttrib;
我在C#中声明非托管函数指针如下:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate eResultCode getAttrib(void** handle, eAttrib attrib, void* value, eAttribType attribType, int valueMaxSize);
internal unsafe getAttrib GetAttrib;
地点:
handle
是指向 C++ 库结构的指针的void **
attrib
模仿指示要检索的属性的 C++ 枚举value
是 C++ 变量的void *
attribType
模仿指示value
的 C++ 类型的 C++ 枚举
valueMaxSize
是一个整数,表示value
指向的变量的大小
我的问题是我不知道如何从 C# 中使用不同数据类型为 void *
(value
变量)调用 GetAttrib()。有时这个变量是 C++ char *
,有时是 C++ int
,还有时候是 C++ enum
等
如果有人能告诉我如何正确分配/填充 char *
(eString
) 和 long
(eLong
) 来自 C# 我认为剩下的部分会落实到位(希望如此)。
最佳答案
首先,eString
是一个特例,因为它是唯一的可变长度类型。其他类型的大小固定,应该更容易检索。
那么让我们从eLong
开始吧。这映射到一个 C++ long
,它又应该可能映射到一个 C# int
- 但这是依赖于体系结构的,所以你的里程我会有所不同。
- 在 C# 中,
int
总是 32 位的,long
总是 64 位的。 - 在 C++ 中,有 no fixed size .如您所知,
int
至少定义为 16 位,long
至少定义为 32 位。在 Visual C++ 中,它们是 both 32-bit不过。
确保您知道自己在做什么,并且尺寸合适。
现在,您需要一个指向非托管函数可以安全写入的不可移动内存的指针。碰巧,值类型 堆栈变量不会被 GC 移动,因此您可以简单地编写以下内容:
internal static unsafe int GetUnits(void** handle)
{
int value;
GetAttrib(handle, eAttrib.eUnits, &value, eAttribType.eLong, sizeof(int));
return value;
}
另一种方法涉及 stackalloc
:
internal static unsafe int GetUnits(void** handle)
{
var buffer = stackalloc int[1];
GetAttrib(handle, eAttrib.eUnits, buffer, eAttribType.eLong, sizeof(int));
return *buffer;
}
如果没有适合的 C# 内置类型,您可以将它与 stackalloc byte[whatever]
一起使用。
这显然是未经测试的,因为我没有库,但它应该可以。
至于字符串,您需要一个托管数组,这样您就可以将它们输入 Encoding.GetString
.是的,您还需要知道编码。我在这里假设 ASCII。
internal static unsafe string GetInvoiceNumber(void** handle)
{
var buffer = new byte[512];
fixed (byte* bufAddr = &buffer[0])
{
GetAttrib(handle, eAttrib.eInvoiceNumber, bufAddr, eAttribType.eString, buffer.Length);
return Encoding.ASCII.GetString(buffer);
}
}
由于托管数组位于托管堆上,您需要固定它,这样它就不会在运行期间被 GC 移动。这就是fixed
statement
关于c# - 从 C# 调用 C++ 函数传递类型剥离的空指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34053797/