开发者

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) 
     开发者_C百科   {
            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?


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.


This is likely because you took a char* pointing to a string literal- which is bad because modifying that memory is undefined behaviour.


Would this work?

public static int test([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] p)


In many cases and as seems to be the situation here (see accepted answer by Remus Rusanu), using the proper marshaling attributes in the API declarations is all that is needed to have the interop "glue" on both ends of the interface start "playing nice with one another" and result in...

  • the proper data values being sent/delivered [good!]
  • Windows not triggering automatic breakpoint upon various suspected
    ... corruption of the heap ... [better! ;-)]

I am adding this answer, 5 months after the original post, because this question and its responses were very useful in fixing a recent interop bug of mine, but failed to mention directly information about the very plausible cause of many interop issues, namely:

Mismatch in the Memory Ownership conventions
(and/or in the memory allocation and freeing methods).

The January 2008 article on MSDN Magazine titled Marshaling between Managed and Unmanaged Code provided me with the info I needed in regards to memory ownership conventions. Indeed this article provides a complete overview of the marshaling process. It covers in specific details and examples the issues of

  • [InAttribute] and [OutAttribute] aka [In] and [Out]
  • Keywords Out and Ref and passing by reference
  • Return values
  • StringBuilder and marshaling
  • Copying and pinning
    (Pinning = optiimization by CLR when it deems it safe to lock data area in the CLR heap and pass corresponding pointers to unmanaged code)
  • Memory Ownership:
    No change allowed vs. In-place change vs. Reference change
    Also, on the need of using CoTaskMemFree() and CoTaskMemAlloc() to free or allocate memory respectively received from or sent to the Managed code.
  • Reverse P/Invoke and delegate lifetime
  • P/Invoke Interop Assistant

This article is very useful because it gathers in a single document and in an accessible fashion information otherwise disseminated across dozen of technically authoritative but dry [and oft' confusing] reference documents (cf. the old but on-point joke about Microsoft's documentation at large).
In short, it makes it a good primer or refresher for casual implementers of interop solutions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜