将包含String数组和Integer数组的Structure传递给C ++ DLL

时间:2010-05-27 06:50:07

标签: c++ vb.net arrays marshalling structure

我遇到了用VB.NET编译到C ++的问题,这是代码:

在C ++ DLL中:

struct APP_PARAM
{
    int numData;
    LPCSTR *text;
    int *values;
};

int App::StartApp(APP_PARAM params)
{
    for (int i = 0; i < numLines; i++)
    {
        OutputDebugString(params.text[i]);
    }
}

在VB.NET中:

  <StructLayoutAttribute(LayoutKind.Sequential)> _
  Public Structure APP_PARAM
    Public numData As Integer
    Public text As System.IntPtr
    Public values As System.IntPtr
  End Structure

  Declare Function StartApp Lib "AppSupport.dll" (ByVal params As APP_PARAM) As Integer

  Sub Main()

    Dim params As APP_PARAM
    params.numData = 3

    Dim text As String() = {"A", "B", "C"}
    Dim textHandle As GCHandle = GCHandle.Alloc(text)
    params.text = GCHandle.ToIntPtr(textHandle)

    Dim values As Integer() = {10, 20, 30}
    Dim valuesHandle As GCHandle = GCHandle.Alloc(values)
    params.values = GCHandle.ToIntPtr(heightHandle)

    StartApp(params)

    textHandle.Free()
    valuesHandle.Free()

  End Sub

我检查了C ++端,OutputDebugString的输出是垃圾,文本数组包含随机字符。这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

GCHandle.Alloc "Allocates a Normal handle for the specified object"“创建托管对象的句柄...阻止托管对象被收集”

您正在寻找的是来自System.Runtime.InteropServices.Marshal的方法,它允许您执行诸如将托管对象复制到非托管代码可访问的内存之类的操作。不幸的是,根据this,你的结构中的指针使得它比其他许多东西更难编组(在某种意义上,许多其他东西可以使用适当的P / Invoke属性自动编组),但它仍然是可能。我试过这个并且它有效:

APP_PARAM param = new APP_PARAM();
string[] text = new string[] { "A", "B", "C" };
param.numData = text.Length;

// Manually allocate an array of pointers, one for each string.  arr holds the array's address.
IntPtr arr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * text.Length);
try
{
  param.text = arr;

  IntPtr[] unmanagedText = new IntPtr[text.Length];
  try
  {
    // Create a null-terminated ANSI string in unmanaged memory for each element in text.
    for (int i = 0; i < text.Length; i++)
      unmanagedText[i] = Marshal.StringToHGlobalAnsi(text[i]);
    // Copy the addresses of the unmanaged strings into the manually allocated array.
    // I don't know of any way to make an unmanaged copy of a managed array in one call.
    Marshal.Copy(unmanagedText, 0, arr, unmanagedText.Length);

    // param now looks like what the C++ code is expecting (except for the array of int).
    StartApp(param);
  }
  finally
  {
    foreach (IntPtr str in unmanagedText)
      Marshal.FreeHGlobal(str);
  }
}
finally
{
  Marshal.FreeHGlobal(arr);
}

您必须为您的int值数组提供类似的分配/免费代码,并使用自己的try / finally块来确保调用FreeHGlobal。

答案 1 :(得分:0)

您需要使用Marshal类中的一种方法。

Dim str As String = "Hello World"
Dim ptr as IntPtr = Marshal.StringToHGlobalAnsi(str)
Try
  SomePInvokeCall(ptr)
Finally
  Marshal.FreeHGlobal(ptr)
End Try