Search code examples
c#c++marshallingdynamic-linking

Marshal C++ "char**" in C#


I'm calling C# method from C++ and passing char** as argument. It has to be char** because I need to return value through parameter.

C# code:

[ExportDll("test", System.Runtime.InteropServices.CallingConvention.StdCall)]

public static int test([MarshalAs(UnmanagedType.AnsiBStr)] ref string p)
{
    Console.WriteLine(p);
}

C++ code to invoke function:

typedef int (__stdcall *MYPROC)(char **); 

VOID main(VOID) 
{ 
    HINSTANCE hinstLib; 
    MYPROC MyProc; 
    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 

    hinstLib = LoadLibrary(TEXT("mydll.dll")); 

    if (hinstLib != NULL) 
    { 
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "test"); 

        if (NULL != ProcAdd) 
        {
            fRunTimeLinkSuccess = TRUE;
            char s1[] = "test"; 
            char *s2 = s1;
            char **s3 = &s2;    
            (MyProc) (s3); 
            cout << s3;
        }
        fFreeResult = FreeLibrary(hinstLib); 
    } 
}

It's simple to pass char* (remove ref in c#, and use char* in c++), but when trying to pass char** i get a runtime error on line where I call the function :(

in c#, Console.WriteLine prints out correct value, but after that, I get an error:

Windows has triggered a breakpoint in COMDynamicLoad.exe.

This may be due to a corruption of the heap, which indicates a bug in COMDynamicLoad.exe or any of the DLLs it has loaded.

This may also be due to the user pressing F12 while COMDynamicLoad.exe has focus.

The output window may have more diagnostic information.

How should I do this?


Solution

  • You declare ref UnmanagedType.AnsiBStr but you expect a char**. This cannot work, since a ref to a BSTR is not a char**. See Default Marshaling for Strings for examples of marshaling declarations. These are possible declarations for an input-output string:

    PassStringRef2([in, out] BSTR *s);
    PassStringRef3([in, out] LPStr *s);
    PassStringRef4([in, out] LPWStr *s);
    

    and the equivalent C# marshaling declarations are:

    PassStringRef2([MarshalAs(UnmanagedType.BStr)]ref String s);
    PassStringRef3([MarshalAs(UnmanagedType.LPStr)]ref String s);
    PassStringRef4([MarshalAs(UnmanagedType.LPWStr)]ref String s);
    

    Your char** declaration is the equivalent of LPStr *s, so the correct marshaling is [MarshalAs(UnmanagedType.LPStr)]ref String s. But a better option is to use BSTR because of the explicit length declaration, and manipulate it in C++ with the BSTR helpers.