views:

108

answers:

3

I have a c++ plus method that generates a value. i am calling this method from a c# application.
The C++ method is like this:

extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[])

The generate method returns an array of chars (sOut[]=returnvalue; return sOut;)

Now I'm calling this method from my c# app:

[DllImport("mydll.dll")]
static extern string generate(string sIn, string sOut);

As you can see, the return type in c# is string. What is happening is that the returned value in c# is not correct and it is corrupted. (The value is correct inside the generate method, but whenever i call it from c# to extract it, i get some erroneous value.)

Is it OK that my method in C# has a string return value while in c++ it's a char*? Please share your comments, it is urgent, thanks.

+3  A: 

Unfortunately, char* can be somewhat ambiguous. (Literally, it's a pointer to a single character, where you may or may not be able to get at other characters by dereferencing the pointer like an array.) Usually, though, it's a pointer to a null-terminated string of single bytes in ANSI encoding (what Microsoft calls an LPStr). By default, P/Invoke marshals instances of System.String as the BStr type, which is like a Unicode Pascal string (with a length at the beginning). You'll need to use the MarshalAs attribute to explicitly specify how you want the strings marshaled between managed and unmanaged code.

Most likely, you'll want to declare it like so:

[DllImport("mydll.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
static extern string generate(
    [MarshalAs(UnmanagedType.LPStr)] string sIn,
    [MarshalAs(UnmanagedType.LPStr)] out string sOut);

However, your function signature is confusing: is it returning the string, or is it setting an output variable? You may have to adjust the P/Invoke declaration to fit your needs.

Daniel Pryden
+1  A: 

This method cannot be called safely from a native C++ application either. It returns a pointer to an array on the stack frame. Any call to another function will overwrite that stack frame and corrupt the array.

This probably works by accident in a C++ program right now because there is no function call after obtaining the return value of this function. This kind of luck is no longer available when the P/Invoke marshaller sits in between.

You will have to redesign the C++ function. You should give it arguments that allows the caller to pass its own buffer to be filled with the result. For example:

extern "C" void __stdcall generate(const char* input, char* output, int outputSize)

Call this in C# code by passing a StringBuilder for the output argument, properly initialized with a sufficient Capacity to receive the string. The outputSize argument ensures that the C++ function can avoid writing past the end of the buffer and corrupt the garbage collected heap. Don't ignore it.

Hans Passant
+2  A: 

Is it OK that my method in C# has a string return value while in c++ it's a char*?

It depends really.

If that C++ method is allocating the memory for the string and expecting the caller to deallocate it then you have problems...big problems. You could try calling Marshal.FreeHGlobal on it, but there is no guarentee that the C++ method actually allocated the memory on the global heap. Most APIs have you feed the pointer back into one of their methods to free the memory.

It is also possible that the char * is pointing to memory which does not need to be freed. In that case using a string should be fine.

You could have the C# declaration return an IntPtr and then call one of the Marhsal.PtrToStringXXX methods.

Brian Gideon