了解C++ DLL 映射到具有联合的 C#

Understanding C++ DLL mapping to C# with unions

本文关键字:C++ DLL 映射 了解      更新时间:2023-10-16

我有以下情况。我有一个在用 C++ 编写的 DLL 中带有联合的结构,我需要将此结构映射到 C# 代码。请记住,我无法更改 DLL 中的代码。下面是一个示例代码来说明正在发生的事情:

  typedef struct {
    int type;
    union {
        int val;
        double val2;
        char *name;
    };
  } BIZARRE;
  __declspec(dllexport) void changeBizarre(BIZARRE *bizarre, int type, const char *v)
  {
      bizarre->type = type;
      if(type == 0) {
          bizarre->val = 5;
      } else if(type == 1) {
          bizarre->val2 = 2.0;
      } else if(type == 2) {
          strncpy(bizarre->name, "test", strlen("test"));
      } else if(type == 3 && strcmp(v, "test") == 0) {
          bizarre->val = 10;
      }    
  }

在 C# 代码中,我完成了以下操作:

[StructLayout(LayoutKind.Explicit)]
public unsafe struct BIZARRE
{
    [FieldOffset(0)]
    public int type;
    [FieldOffset(8)]
    public int val;
    [FieldOffset(8)]
    public double val2;
    [FieldOffset(8)]
    public char *name;
}
[DllImport("proj.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void changeBizarre(ref BIZARRE bizarre, int type, char *name);
unsafe static void Main()
{
   char[] v = "test".ToCharArray();
   bizarre.type = 0;
   bizarre.val = 0;
   bizarre.val2 = 0.0;
   fixed (char* address = v)
   {
     bizarre.name = address;
     changeBizarre(ref bizarre, 3, &bizarre.name);
     Console.WriteLine("{0}", bizarre.val);
   }
   Console.ReadKey();
}

现在,如果您通过传递类型 = 2 来运行此代码,并尝试打印 bizarre.name,它将显示垃圾字符,如果您传递类型 = 3,显然 DLL 无法获取 bizarre.name 指向的内容,我相信这两种行为具有相同的原因,但我不知道它是什么。

char数组在 C 中不是真正的char数组,实际上 C# 将使用wchar_t类型(16 位字符)。

使用wchar_t的东西更改您的 C 代码:

typedef struct {
    int type;
    union {
        int val;
        double val2;
        wchar_t *name;
    };
} BIZARRE;
__declspec(dllexport) void changeBizarre(BIZARRE *bizarre, int type, const wchar_t *v)
{
    bizarre->type = type;
    if (type == 0) {
        bizarre->val = 5;
    }
    else if (type == 1) {
        bizarre->val2 = 2.0;
    }
    else if (type == 2) {
        wcsncpy(bizarre->name, L"test", wcslen("test"));
    }
    else if (type == 3 && wcscmp(v, L"test") == 0) {
        bizarre->val = 10;
    }
}

使用 byte 类型而不是 char 更改 C# 代码(如 Passant @Hans所述):

[StructLayout(LayoutKind.Explicit)]
public unsafe struct BIZARRE
{
    [FieldOffset(0)]
    public int type;
    [FieldOffset(8)]
    public int val;
    [FieldOffset(8)]
    public double val2;
    [FieldOffset(8)]
    public byte* name;
}

class Program
{
    [DllImport("UnionMapping_dll.dll", CallingConvention = CallingConvention.Cdecl)]
    public unsafe static extern void changeBizarre(ref BIZARRE bizarre, int type, byte* name);
    static unsafe void Main(string[] args)
    {
        BIZARRE bizarre;
        byte[] v = Encoding.ASCII.GetBytes("test");
        bizarre.type = 0;
        bizarre.val = 0;
        bizarre.val2 = 0.0;
        fixed (byte* address = v)
        {
            bizarre.name = address;
            changeBizarre(ref bizarre, 3, bizarre.name);
            Console.WriteLine("{0}", bizarre.val);
        }
        Console.ReadKey();
    }
}

C++代码在结构中具有联合 - 用于互操作目的的 C# 等效项应反映:

  using System.Runtime.InteropServices;
  public struct BIZARRE
  {
    public int type;
    [StructLayout(LayoutKind.Explicit)]
    public struct AnonymousStruct
    {
        [FieldOffset(0)]
        public int val;
        [FieldOffset(0)]
        public double val2;
        [FieldOffset(0)]
        public string name;
    }
  }