【问题标题】:Marshal struct from C to C#从 C 到 C# 的元帅结构
【发布时间】:2014-06-21 06:53:02
【问题描述】:

编辑:我过度简化了我的示例...在实际代码中,我在没有正确使用 wcscpy_s 的情况下为 strMyStringx 赋值,因此我没有分配值,而是传递了指针,此时值超出了范围正在编组到托管代码中...

我正在尝试编组一个具有从 C 到 C# 的三个字符串属性的结构,但我无法在 C# 中正确获得该结构的定义。所有属性都打印为垃圾。我编组错误还是我的属性类型错误?

我的自定义结构:

typedef struct _MY_STRUCT_STRING {
    LPWSTR strMyString1;
    LPWSTR strMyString2;
    LPWSTR strMyString3;
}MY_STRUCT_STRING, *PMY_STRUCT_STRING;

我的 C 函数返回一个指向该结构的指针数组:

bool bEnumerateString(OUT LONG &i_arr_size, OUT PMY_STRUCT_STRING* &pArrStringStruct)
{
// [...] function simplified to demonstrate building a pointer to an array of struct*
long i_arr_size = 3

PMY_STRUCT_STRING *ptr_arr_string = (PMY_STRUCT_STRING *)malloc(sizeof(PMY_STRUCT_STRING)* i_arr_size);

for (int i = 0; i < i_arr_size; i++) {
    ptr_arr_string[i] = (PMY_STRUCT_STRING)malloc(sizeof(MY_STRUCT_STRING));
    ptr_arr_string[i]->strMyString1 = L"String 1"; // This would work. In the real code I was assigning values from another array and mistakenly passed the pointer rather than doing wcscpy_s
    ptr_arr_string[i]->strMyString2 = L"String 2";
    ptr_arr_string[i]->strMyString3 = L"String 3";
}

pArrStringStruct = ptr_arr_string;

return true;
}

C#:

    //Import the DLL with my function
    [DllImport("My.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "bEnumerateString")]
    internal static extern bool bEnumerateString(out long count, out IntPtr pArrStringStruct);

     // Define the C# equivalent of the C struct
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct MY_STRUCT_STRING
    {
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string strMyString1;
    }

   [...]

    // Code to marshal (try... catch etc removed for succinctness)
   IntPtr pArrStruct = IntPtr.Zero;
   long lCount = 0;

   bool bResult = false;
   bResult = bEnumerateString(out lCount, out pArrStruct);

   if (!bResult)
   {
    // Marshal to deref pointer
       IntPtr[] pArrStructList = new IntPtr[lCount];
       for (ulong i = 0; i < (ulong)lCount; i++)
       {
            pArrStructList[i] = Marshal.ReadIntPtr(pArrStruct, Marshal.SizeOf(typeof(IntPtr)) * (int)i);
       }


    // Marshal pointers to struct
       var lstMyStringStrct = new List<MY_STRUCT_STRING>(pArrStructList.Length);

       foreach (IntPtr ptr in pArrStructList)
       {         
            lstMyStringStrct.Add((MY_STRUCT_STRING)Marshal.PtrToStructure(ptr, typeof(MY_STRUCT_STRING)));
       }

    // Enumerate struct
       foreach (MY_STRUCT_STRING myStr in lstMyStringStrct)
       {
           // All of these outputs are garbage
           Console.WriteLine("strMyString1: " + myStr.strMyString1);
           Console.WriteLine("strMyString2: " + myStr.strMyString2);
           Console.WriteLine("strMyString3: " + myStr.strMyString3);
        }

    }

【问题讨论】:

  • LPWSTR 是 wchar_t[],而不是 wchar_t*。删除 [MarshalAs] 属性。并注意内存泄漏,没有人为你调用 free()。您只能通过在本机代码中使用 CoTaskMemAlloc() 而不是 malloc() 或公开调用 free() 的函数来取得成功。
  • @HansPassant - 删除 MarshalAs 属性会导致 Marshal.PtrToStructure 引发 AccessViolation 异常。使用 CoTaskMemAlloc() 会有帮助吗?我没有释放内存,因为我正在尝试使用更窄的范围进行故障排除,但释放内存是否有助于编组?

标签: c# c marshalling


【解决方案1】:

我看到一个问题。您的 C++ 结构使用 LPWSTR(指针),而您的 C# 代码需要固定大小的 char 数组。

从以下位置更改您的字符串:

[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;

当 C++ 结构定义为:

char strMyString1[8];

到:

[MarshalAsAttribute(UnmanagedType.LPWStr)]
public string strMyString1;

【讨论】:

  • 这是我最初的想法,但是使用该属性会导致带有 Marshal.PtrToStructure 的 ExecutionEngineException
  • 原来我有一些搞砸的复制指针而不是做 wcscpy_s 这导致它在 Win32 控制台应用程序中工作(因为指针仍在范围内)但是一旦它在 DLL 中就会失败并且被编组到托管代码中。
猜你喜欢
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多