Passing a structure containing a String array and an Integer array to a C ++ DLL

I have problems with marshaling in VB.NET in C ++, here is the code:

In the 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]);
    }
}

In 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

I checked the C ++ side, the output from OutputDebugString is garbage, the text array contains random characters. What is the right way to do this?

+1
source share
2 answers

GCHandle.Alloc "Selects the normal descriptor for the specified object," which "creates a managed object descriptor ... that prevents the collection of the managed 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 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

Marshal.

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

Source: https://habr.com/ru/post/1793629/


All Articles