Передача структуры, содержащей массив String и массив Integer, в C ++ DLL - PullRequest
1 голос
/ 27 мая 2010

У меня проблемы с маршалингом в 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 ]

1 голос
/ 28 мая 2010

GCHandle.Alloc "Выделяет дескриптор Normal для указанного объекта" , который "создает дескриптор управляемого объекта ..., который не позволяет управляемому объекту собирать ".

То, что вы ищете, это методы из 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.

0 голосов
/ 27 мая 2010

Вам необходимо использовать один из методов класса Marshal.

Dim str As String = "Hello World"
Dim ptr as IntPtr = Marshal.StringToHGlobalAnsi(str)
Try
  SomePInvokeCall(ptr)
Finally
  Marshal.FreeHGlobal(ptr)
End Try
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...