c# - 从 C# 调用 C++ 函数传递类型剥离的空指针

标签 c# c++

我正在尝试使用用 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/

相关文章:

c# - 从 Xamarin 表单启动 android 服务

c# - 包装托管代码以供非托管使用

c++ - 为什么按值调用示例不修改输入参数?

c++ - QSerialPort 的 readAll() 不包括最后发送的响应

c# - 接口(interface)不同时实现开闭原则

c# - 在 ASP.NET MVC 4 中使用共享 View

c# - 企业库错误

c++ - 有哪些库可用于解析c++以提取类型信息

c++ - 检查 vector 中的一组值

c++ 模板在 vs express 2015 中看不到