【问题标题】:Passing an struct array into C++ DLL from C#从 C# 将结构数组传递到 C++ DLL
【发布时间】:2011-06-01 06:43:46
【问题描述】:

我正在尝试将结构数组传递到 C++ DLL 并遇到问题。我一直试图弄清楚这几天,但无济于事。我可以从 C++ 中很好地获取数据,但在尝试使用 .NET 获取结构数组时遇到了问题。

C++ 原型是:

static __declspec(dllexport) int SocketAPI::api_get_data(int iSize, buffer_node *data); 

在我的 C# 代码中,我将函数定义为:

[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, buffer_node[] data);

我的结构是buffer_node,定义为:

[StructLayout(LayoutKind.Sequential, Size = 23), Serializable]
public struct header
{
    // HEADER
    public UInt16 h_type; 
    public UInt32 frame_num;  
    public UInt16 count_1pps;   
    public byte data_options;   
    public byte project_type;   
    public byte tile_num;     
    public byte tile_set;          
    public byte total_rows;     
    public byte total_cols;      
    public byte num_rows;         
    public byte num_cols;       
    public byte first_row;        
    public byte first_col;      
    public UInt16 num_sensors;      
    public UInt16 num_data_bytes; 
    public byte h_checksum;
}

[StructLayout(LayoutKind.Sequential, Size = 25), Serializable]
public struct footer
{
    // FOOTER
    public UInt16 f_type;  
    public byte ts_len;           
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public byte[] ts_array;
    public byte frame_status;
    public byte f_checksum;     
}

[StructLayout(LayoutKind.Sequential, Size = 51), Serializable]
public struct buffer_node
{
    // HEADER
    public header data_header;

    // DATA
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] data;

    // FOOTER
    public footer data_footer;
}

如果尝试了以下导入:

// See buffer, but everything is 0 - ie. not being populated
unsafe static extern int api_get_data(int iSize, buffer_node[] data); 

// fails somewhere in the API
static extern int api_get_data(int iSize, out buffer_node[] data); 

static extern int api_get_data(int iSize, ref buffer_node[] data);

我的 C# GetData 程序目前如下所示:

// Get current data size
int iSize = api_is_data_available();

// Create buffer to hold the data
buffer_node[] buf_data = new buffer_node[iSize];

for (int i = 0; i < iSize; i++)
{
    buf_data[i].data = new byte[3];
    buf_data[i].data_footer.ts_array = new byte[20];
}

// Get the data
//int iStructSize = Marshal.SizeOf(buf_data[0]);
//IntPtr bufNodePtr = IntPtr.Zero;
//IntPtr buffer = Marshal.AllocHGlobal(iStructSize * iSize);
//api_get_data(iSize, buffer);
//for (int i = 0; i < iSize; i++)
//{
//    IntPtr ptr = new IntPtr(buffer.ToInt64() + iStructSize * i);
//    buf_data[i] = (buffer_node)Marshal.PtrToStructure(ptr, typeof(buffer_node));
//}

//api_get_data(iSize, buf_data); // See buffer, but everything is 0 - ie. not being populated
// api_get_data(iSize, out buf_data); // fails no error
api_get_data(iSize, ref buf_data); // fails no error
// api_get_data(iSize, ref buf_data);

// Print the data
for (int i = 0; i < iSize; i++)
{
    StringBuilder sb = new StringBuilder();
    sb.Append("Tile Number: " + Convert.ToString(buf_data[i].data_header.tile_num));
    AppendTextBox(sb.ToString());
}

再次感谢您。任何帮助将不胜感激,因为我认为这是一个简单的任务真的让我陷入困境!

【问题讨论】:

    标签: c# c++ arrays dll struct


    【解决方案1】:

    您必须在 [DllImport] 属性中使用 CallingConvention 属性。默认是 StdCall,这里需要 Cdecl,因为 C++ 声明没有使用 __stdcall。

    【讨论】:

    • 嗨。对不起。我根本没有遵循。你能解释一下吗?
    • Google +dllimportattribute +callingconvention。第三击看起来不错。
    • 这实际上是正确的答案。我有一个类似的问题,并用这个解决方案解决了。在这种情况下添加:[DllImport("SocketAPI.dll", CallingConvention=CallingConvention.Cdecl)]
    【解决方案2】:

    如果int iSize 是元素数组的大小(例如data.Length),请尝试使用MarshallAs.SizeParamIndex。这将告诉编组器数据中应该有多少元素。

    [DllImport("SocketAPI.dll")]
    static extern int api_get_data(int iSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)]  buffer_node[] data);
    

    有关数组的更多信息mashalled at MSDN

    【讨论】:

    • 这使数组再次充满了 0。我将在您提供的链接上阅读更多内容。谢谢。
    【解决方案3】:

    对 buffer_node[] 数据参数使用 [In, Out] 属性:

    [DllImport("SocketAPI.dll")]
    static extern int api_get_data(int iSize, [In, Out] buffer_node[] data);
    

    【讨论】:

      【解决方案4】:

      带有refout 的那些不起作用,因为它们传递的是指向引用的指针,而不是指向第一个元素的指针。


      编辑 1:我刚刚注意到,你不能像现在这样传递数组——结构中的托管数组通常不会按照你想要的方式进行编组。当我想到一个解决方案时,我会写一个解决方案,但我认为您将不得不手动整理。


      编辑 2:如果您能够使用不安全的代码,那么这应该可以解决问题:将所有内容从 ByValArray 更改为 fixed byte[],然后使用此代码:

      [StructLayout(LayoutKind.Sequential, Size = 23), Serializable]
      public struct header
      {
          // HEADER
          public UInt16 h_type; 
          public UInt32 frame_num;  
          public UInt16 count_1pps;   
          public byte data_options;   
          public byte project_type;   
          public byte tile_num;     
          public byte tile_set;          
          public byte total_rows;     
          public byte total_cols;      
          public byte num_rows;         
          public byte num_cols;       
          public byte first_row;        
          public byte first_col;      
          public UInt16 num_sensors;      
          public UInt16 num_data_bytes; 
          public byte h_checksum;
      }
      
      [StructLayout(LayoutKind.Sequential, Size = 25), Serializable]
      public struct footer
      {
          // FOOTER
          public UInt16 f_type;  
          public byte ts_len;           
          public unsafe fixed byte ts_array[20];
          public byte frame_status;
          public byte f_checksum;     
      }
      
      [StructLayout(LayoutKind.Sequential, Size = 51), Serializable]
      public struct buffer_node
      {
          // HEADER
          public header data_header;
      
          // DATA
          public unsafe fixed byte data[3];
      
          // FOOTER
          public footer data_footer;
      }
      
      unsafe static extern int api_get_data(int iSize, buffer_node* pData);
      
      
      //...
      
      // Get current data size
      int iSize = api_is_data_available();
      
      // Create buffer to hold the data
      buffer_node[] buf_data = new buffer_node[iSize];
      
      unsafe
      {
          fixed (buffer_node* pBufData = buf_data)
          {
              api_get_data(iSize, pBufData); // fails no error
          }
      }
      

      (您必须将声明更改为指向元素的指针。)


      编辑 3:我刚刚注意到...你试过这样说 [Out] 吗?

      [DllImport("SocketAPI.dll")]
      static extern int api_get_data(int iSize, [Out] buffer_node[] data);
      

      这可能会奏效,而不会像我上面所做的那样痛苦。

      旁注:说Size = 23 不会做任何事情,除非您也更改对齐方式,因为结构将被填充以达到默认对齐方式。

      【讨论】:

      • 嗨。感谢你的回复。我的偏好是使用安全代码。但是,在这一点上,我只想让它工作。我不明白如何实现您在“编辑 2”中编写的内容。谢谢。
      • 嗯......“[Out]”可能已经做到了。我将不得不做更多的测试。这是一个很难在谷歌上搜索的。 [出] 是什么意思?它和外面有什么不同?
      • 看起来第一个元素正确返回,之后有些东西误入歧途。我会用它一段时间,看看我的尺寸是否有问题。谢谢!
      • 做了一些彻底的检查。 buffer_node 数组中的第一个元素是完整的。其余元素已损坏。
      • 关于[Out] 属性:这意味着您希望将数据从C 代码back 编组到原始数组中。 (运行时不传递直接指针;它在一个或两个方向复制数据。)如果你说out,那意味着你得到了一个指向 new 的指针数组,这里不是这种情况。关于尺寸:如果你真的需要奇数尺寸,你必须使用Pack属性:[StructLayout(LayoutKind.Sequential, Size = 23, Pack = 1)];否则它不会做你想做的。
      【解决方案5】:

      我不得不将空数组从 C# 传递到 dll 中的 C 函数时遇到了同样的问题。然后该函数将返回指向数组的第一个元素的指针,该数组中填充了结构体。

      这是我声明外部函数的方式:

      [DllImport(LIB_NAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "getData")]
      unsafe extern void getData(IntPtr data, ref UInt32 dataLen);
      

      有问题的结构:

      [StructLayout(LayoutKind.Sequential)]
      internal struct DataC
      {
          internal UInt16 xRes, yRes;
          internal fixed float rot[9];
      }
      

      这就是我调用函数以及将 IntPtr 转换为我的结构的方式:

      unsafe
      {
          UInt32 dataLen = 10;
          IntPtr dataPtr = Marshal.AllocHGlobal((int)dataLen * Marshal.SizeOf(typeof(DataC)));
          getData(dataPtr, ref dataLen);
          // check here for null, obviously
          DataC* dataArr = (DataC*)dataPtr;
          for (int i = 0; i < dataLen; i++)
          {
              DataC data = dataArr[i];
              // I fill a managed class/struct with the unmanaged data and add it to a List or whatever
              result.Add(new Data(data->xRes, data->yRes, data->rot[0], ...));
          }
          // As we have the data in managed memory now, we free the allocated space
          Marshal.FreeHGlobal(dataPtr);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        • 2021-09-13
        • 2011-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多