1

As the title suggests I would like to let a string array be described in a function which is in a C++ DLL.

In my actual attempt it runs without exception, but my strArray does not contain any objects after the C++ Function call.

My C# Code:

var strArray = new StringBuilder[100];
for (int i = 0; i < strArray .Length; i++)
{
         strArray[i] = new StringBuilder(50);
}
modifyStringInCpp(strArray);

[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private extern static void modifyStringInCpp(StringBuilder[] strA);

My C++ Code:

extern "C" { 
    __declspec(dllexport) void __cdecl modifyStringInCpp(char** strA)
    {
        for (size_t i = 0; i < 100; i++)
        {
            strcpy(strA[i], "testString");
        }
    }
}

What do I need to change?

2
  • I doubt that a normal 1-byte char will convert safely from a .NET string which consists of two-byte chars (IIRC). Commented Mar 21, 2017 at 13:09
  • 1
    @Anne Sadly no marshaling for StringBuilder[] :-( I thought it would work Commented Mar 21, 2017 at 14:07

1 Answer 1

3

Sadly there is no default marshaling for StringBuilder[]... It then becomes a PAIN... A PAIN!!!

You have to do everything manually! :-(

[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private extern static void modifyStringInCpp(IntPtr[] strA);

and then:

var strArray = new byte[100][];

for (int i = 0; i < strArray.Length; i++) 
{
    // 49 characters + NUL
    strArray[i] = new byte[50]; // or a size you choose
}

var handles = new GCHandle[strArray.Length];

string[] strings;

try 
{
    var ptrs = new IntPtr[strArray.Length];

    for (int i = 0; i < strArray.Length; i++) 
    {
        handles[i] = GCHandle.Alloc(strArray[i], GCHandleType.Pinned);
        ptrs[i] = handles[i].AddrOfPinnedObject();
    }

    modifyStringInCpp(ptrs);

    strings = new string[strArray.Length];

    for (int i = 0; i < strArray.Length; i++) 
    {
        strings[i] = Marshal.PtrToStringAnsi(ptrs[i]);
    }
} 
finally 
{
    for (int i = 0; i < strArray.Length; i++) 
    {
        if (handles[i].IsAllocated) 
        {
            handles[i].Free();
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

@xanatos new StringBuilder(50) results in an array of length 51 when marshalled. See msdn.microsoft.com/en-US/library/s9ts558h.aspx which says: For example, if you initialize a StringBuilder buffer to a capacity of N, the marshaler provides a buffer of size (N+1) characters. The +1 accounts for the fact that the unmanaged string has a null terminator while StringBuilder does not.
@DavidHeffernan Right :-) I had forgotten...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.