Search code examples
c#chardllimportpointer-to-pointer

char** DllImport fails


I would like to DllImport the following function. Nevertheless, "ret" returns true, but my string array seems to be empty, so I think I may need some marshaling. Any tip is welcome! Thanks in advance :)

The C function:

bool getBootLog(char **a1);

The code below is for testing and does not work properly.

The DllImport:

[DllImport("ext.dll")]
public static extern bool getBootLog(string[] bootLog);

Current code:

        string[] bootLog = new string[1024 * 1024];
        bool ret = getBootLog(bootLog);

        foreach (string s in bootLog)
        {
            Debug.WriteLine(s);
        }

2 more tries which do NOT work:

var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
try
{
    getBootLog(out ptr);
    var deref1 = (string)Marshal.PtrToStringAnsi(ptr);
    Debug.WriteLine(deref1);
}
finally
{
    Marshal.FreeHGlobal(ptr);
}

var ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
try
{
    getBootLog(out ptr2);
    var deref1 = (IntPtr)Marshal.PtrToStructure(ptr2, typeof(IntPtr));
    var deref2 = (string[])Marshal.PtrToStructure(deref1, typeof(string[]));
    Debug.WriteLine(deref2);
}
finally
{
    Marshal.FreeHGlobal(ptr2);
}

mohsen's idea:

[DllImport("Ext.dll")]
public static extern bool getBootLog(StringBuilder bootLog);

try
{
    int bufferSize = 50;
    StringBuilder bootLog = new StringBuilder(" ", bufferSize);
    Debug.WriteLine("Prepared bootLog...");
    getBootLog(bootLog);
    Debug.WriteLine("Bootlog length: " + bootLog.Length);
    string realString = bootLog.ToString();
    Debug.WriteLine("Bootlog: " + realString);
}
catch(Exception ex)
{
    Debug.WriteLine("Xception: " + ex.ToString());
}

results in:

Prepared bootLog... Bootlog length: 0 Bootlog:


Solution

  • Fix up the declaration:

    [DllImport("ext.dll", CharSet = CharSet.Ansi)]
    public static extern bool getBootLog(ref IntPtr bootLogPtr);
    

    The attempted lines in the edited version of the code looks to be incorrect.

    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
    

    Really, the size of the buffer should be specified.

    var ptr = Marshal.AllocHGlobal(100); // Set it to 100 bytes, maximum!
    

    Write the length in to the ptr, as its a C style null terminated string.

    Marshal.WriteByte(ptr, 100, 0);
    

    Then invoke the call which would be, top of my head:

    IntPtr ptrBuf = ptr;
    getBootLog(ref ptrBuf);
    

    Copy the contents of the buffer by ptrBuf into string variable:

    string sBootLog = Marshal.PtrToStringAnsi(ptrBuf);
    

    Clean up the unmanaged memory contents:

    Marshal.FreeHGlobal(ptr);