Search code examples
c#c++interoppinvokemarshalling

How to marshall pointer to array of char[256] from C++ to C#


I have C++ method which has following signature:

typedef char TNameFile[256];

void Foo(TNameFile** output);

I've run out of ideas how to marshall it.


Solution

  • Assuming they return an empty string as last element:

    static extern void Foo(ref IntPtr output);
    
    IntPtr ptr = IntPtr.Zero;
    Foo(ref ptr);
    while (Marshal.ReadByte(ptr) != 0)
    {
       Debug.Print(Marshal.PtrToStringAnsi(ptr, 256).TrimEnd('\0'));
       ptr = new IntPtr(ptr.ToInt64() + 256);
    }
    

    EDIT: Since I've written the above code on my smartphone, I tested the code this morning and it seems like it should work (I just had to add the TrimEnd('\0')). Here is my test case:

    class Program
    {
        const int blockLength = 256;
    
        /// <summary>
        /// Method that simulates your C++ Foo() function
        /// </summary>
        /// <param name="output"></param>
        static void Foo(ref IntPtr output)
        {
            const int numberOfStrings = 4;
            byte[] block = new byte[blockLength];
            IntPtr dest = output = Marshal.AllocHGlobal((numberOfStrings * blockLength) + 1);
            for (int i = 0; i < numberOfStrings; i++)
            {
                byte[] source = Encoding.UTF8.GetBytes("Test " + i);
                Array.Clear(block, 0, blockLength);
                source.CopyTo(block, 0);
                Marshal.Copy(block, 0, dest, blockLength);
                dest = new IntPtr(dest.ToInt64() + blockLength);
            }
            Marshal.WriteByte(dest, 0); // terminate
        }
    
        /// <summary>
        /// Method that calls the simulated C++ Foo() and yields each string
        /// </summary>
        /// <returns></returns>
        static IEnumerable<string> FooCaller()
        {
            IntPtr ptr = IntPtr.Zero;
            Foo(ref ptr);
            while (Marshal.ReadByte(ptr) != 0)
            {
                yield return Marshal.PtrToStringAnsi(ptr, blockLength).TrimEnd('\0');
                ptr = new IntPtr(ptr.ToInt64() + blockLength);
            }
        }
    
        static void Main(string[] args)
        {
            foreach (string fn in FooCaller())
            {
                Console.WriteLine(fn);
            }
            Console.ReadKey();
        }
    }
    

    One question remains: who is going to free the buffer?