Reputation: 371
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?
Upvotes: 3
Views: 5866
Reputation: 75125
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...
... 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
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.
Upvotes: 0
Reputation: 294287
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.
Upvotes: 3
Reputation: 7342
Would this work?
public static int test([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] p)
Upvotes: 0
Reputation: 146930
This is likely because you took a char*
pointing to a string literal- which is bad because modifying that memory is undefined behaviour.
Upvotes: 3