Marshalling C#/ C ++之间的复杂结构 [英] Marshalling Complex structures between C#/C++

查看:264
本文介绍了Marshalling C#/ C ++之间的复杂结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从C ++中填充一个结构数组,并将结果传递给C#。

I'm trying to populate an array of structures from C++ and pass the result back to C#.

我想可能创建一个结构体前进的方式大多数例子我遇到使用结构(但传递基本类型)。我已尝试以下操作,但没有运气到目前为止。

I thought maybe creating a struct with an array of structures maybe the way forward as most examples I have come across use structures(but passing basic types). I have tried the following but no luck so far.

找到一个例子: http://limbioliong.wordpress.com/2011/08 / 20 / pass-a-pointer-to-a-structure-from-c-to-c-part-2 /?relatedposts_exclude = 542

我在C#中有以下内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpDLLCall
{
  class Program
  {

[StructLayout(LayoutKind.Sequential,Pack=8)]
public struct Struct1
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public double[] d1;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public double[] d2;
}

[StructLayout(LayoutKind.Sequential)]
public struct TestStructOuter
{
     public int length;
     public IntPtr embedded;
}

 static void Main(string[] args)
 {
    Program program = new Program();
    program.demoArrayOfStructs();
 }

public void demoArrayOfStructs() 
{
    TestStructOuter outer = new TestStructOuter();
    testStructOuter.embedded = new Struct1[10];
    outer.length = 10;
    outer.embedded = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Struct1)) * 10);
    Marshal.StructureToPtr(outer, outer.embedded, false);
    testAPI2(ref outer);
    outer = (TestStructOuter)(Marshal.PtrToStructure(outer.embedded, typeof(TestStructOuter)));
    Marshal.FreeHGlobal(outer.embedded);
 }

[DllImport(@"C:\CPP_Projects\DLL\DLLSample\Release\DLLSample.dll")]
static extern void testAPI2(IntPtr pTestStructOuter);
}
}



在头文件中的C ++中

In C++ in the header i have

#ifdef DLLSAMPLE_EXPORTS
#define DLLSAMPLE_API __declspec(dllexport)
#else
#define DLLSAMPLE_API __declspec(dllimport)
#endif

#include <iostream>
using namespace std;

#pragma pack(1)
struct struct1
{
    double d1[];
    double d2[];
};

struct TestStructOuter
{
    struct1* embedded;
};

extern "C"
{   
    DLLSAMPLE_API void __stdcall testAPI2(TestStructOuter* pTestStructOuter);
}

在cpp中我有:

#include "stdafx.h"
#include "DLLSample.h"

__declspec(dllexport) void __stdcall testAPI2(TestStructOuter* pTestStructOuter)
{
    // not sure that this is necessary
    // for (int i = 0; i < 10 ; ++i)
    // {    
    //     pTestStructOuter->embedded = new struct1;        
    // }

    for (int i = 0; i < 10 ; ++i)
    {
            struct1 s1;

            for (int idx = 0; idx < 10; ++idx)
            {
                    s1.d1[i] = i+0.5;
                    s1.d2[i] = i+0.5;
            }

            pTestStructOuter->embedded[0] = s1;
    }
}

这似乎不工作的错误,我得到:
参数不正确(来自HRESULT的异常:0x80070057(E_INVALIDARG))

This doesn't seem to work the error i get: The parameter is incorrect.(Exception from HRESULT:0x80070057 (E_INVALIDARG))

这可能意味着它无法识别结构数组。任何想法如何我可以做到这一点?谢谢。

Which probably means that its not recognizing the array of structures. Any ideas how I can do this? Thanks.

推荐答案

好,我有一个工作示例。我把这个发布为另一个答案,因为它是一个非常不同的方法。

Okay, I have a working sample. I'm posting this as another answer because it's a very different approach.

所以,在C ++方面,我有这个头文件:

So, on the C++ side, I've got this header file:

struct Struct1
{
  int d1[10];
  int d2[10];
};

extern "C" __declspec(dllexport) void __stdcall 
  TestApi2(int* pLength, Struct1 **pStructures);

以下代码:

__declspec(dllexport) void __stdcall 
  TestApi2(int* pLength, Struct1 **pStructures)
{
  int len = 10;

  *pLength = len;
  *pStructures = (Struct1*)LocalAlloc(0, len * sizeof(Struct1));

  Struct1 *pCur = *pStructures;

  for (int i = 0; i < len; i++)
  {
    for (int idx = 0; idx < 10; ++idx)
    {
      pCur->d1[idx] = i + idx;
      pCur->d2[idx] = i + idx;
    }

    pCur ++;
  }
}

现在,重要的是 LocalAlloc 这实际上是我已经有所有的问题,因为我分配内存错误的地方。 LocalAlloc 是.NET调用的方法,当它 Marshal.AllocHGlobal 时,这是非常方便,因为这意味着我们可以使用调用者中的内存,并根据需要处理它。

Now, the important bit is LocalAlloc. That's actually the place where I've had all the issues, since I allocated the memory wrong. LocalAlloc is the method .NET calls when it does Marshal.AllocHGlobal, which is very handy, since that means we can use the memory in the caller and dispose of it as needed.

现在,此方法允许您返回任意长度的结构数组。相同的方法可用于例如。返回一个结构数组的结构,你只是更深入。关键是 LocalAlloc - 这是你可以使用 Marshal 类容易访问的内存,它的内存不是

Now, this method allows you to return an arbitrary length array of structures. The same approach can be used to eg. return a structure of an array of structures, you're just going deeper. The key is the LocalAlloc - that's memory you can easily access using the Marshal class, and it's memory that isn't thrown away.

您还必须返回数组的长度的原因是因为没有办法知道有多少数据要返回。这是非托管代码中的一个常见的问题,如果你已经做过任何P / Invoking,你知道这一切。

The reason you have to also return the length of the array is because there's no way to know how much data you're "returning" otherwise. This is a common "problem" in unmanaged code, and if you've ever done any P/Invoking, you know everything about this.

现在,C# 。我试着以一种很好的方式做到这一点,但问题是结构数组只是不可Blittable,这意味着你不能简单地使用 MarshalAs(UnmanagedType.LPArray,... )。因此,我们必须按 IntPtr 的方式。

And now, the C# side. I've tried hard to do this in a nice way, but the problem is that arrays of structures simply aren't blittable, which means you can't simply use MarshalAs(UnmanagedType.LPArray, ...). So, we have to go the IntPtr way.

定义如下:

[StructLayout(LayoutKind.Sequential)]
public class Struct1
{
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
  public int[] d1;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
  public int[] d2;
}        

[DllImport(@"Win32Project.dll", CallingConvention = CallingConvention.StdCall)]
static extern void TestApi2(out int length, out IntPtr structs);

基本上,我们得到一个指向array长度的指针,到数组的第一个元素。

Basically, we get a pointer to the length of the "array", and the pointer to the pointer to the first element of the array. That's all we need to read the data.

代码如下:

int length;
IntPtr pStructs;

TestApi2(out length, out pStructs);

// Prepare the C#-side array to copy the data to
Struct1[] structs = new Struct1[length];

IntPtr current = pStructs;
for (int i = 0; i < length; i++)
{
  // Create a new struct and copy the unmanaged one to it
  structs[i] = new Struct1();
  Marshal.PtrToStructure(current, structs[i]);

  // Clean-up
  Marshal.DestroyStructure(current, typeof(Struct1));

  // And move to the next structure in the array
  current = (IntPtr)((long)current + Marshal.SizeOf(structs[i]));
}

// And finally, dispose of the whole block of unmanaged memory.
Marshal.FreeHGlobal(pStructs);

如果你想真正返回结构数组的结构,唯一的变化是方法参数移动到包装结构中。方便的事情是.NET可以自动处理包装器的编组,较不方便的事情是它不能处理内部数组,所以你再次需要使用length + IntPtr手动管理这些。

The only thing that changes if you want to really return a structure of an array of structures is that the method parameters move into the "wrapping" structure. The handy thing is that .NET can automatically handle the marshalling of the wrapper, the less-handy thing is that it can't handle the inner array, so you again have to use length + IntPtr to manage this manually.

这篇关于Marshalling C#/ C ++之间的复杂结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆