views:

239

answers:

3

I have this function in Visual C++ DLL

char * __stdcall GetMessage(int id) {
char buffer[250];
.
.
.
strcpy(buffer, entry); // entry is a char * that has allocated 250 chars
return (char *)buffer;
}

i am trying to import this function from C# with the following code

[DllImport("mydll.dll", CharSet=CharSet.Ansi)]
public static extern string GetMessage(int id);

I am trying to present this in a MessageBox it always has strange symbols in the end of the string. Any suggestions how to overcome this problem?

+1  A: 

From the DLL you are returning an array of chars i.e. buffer which is on the stack within the GetMessage function, and when the return is executed the data is messed up since the buffer is popped off the stack and is local to the stack also, hence the strange characters you are seeing. There is two ways you can fix this, change the buffer variable declaration to be of a pointer and malloc it before returning it or declare it as a static.

char *buffer;
if (!(buffer = (char *)malloc((250 * sizeof(char)) + 1)){
   // Handle the out of memory condition
}
// Now the buffer has 250 + 1 for null terminating character.

Or

static char buffer[250];

Also can you clarify entry pointer variable - is that global?

Hope this helps, Best regards, Tom.

tommieb75
A: 

Try using a StringBuilder for the return type and leave out the CharSet attribute:

[DllImport("mydll.dll")]
public static extern StringBuilder GetMessage(int id);
Darin Dimitrov
Won't help with pointer-to-dead-local issue at all.
Pavel Minaev
+4  A: 

I'll re-emphasize the fact that your C++ code is invalid. You are returning a pointer to a local variable on a stack frame that is no longer valid when the function returns. That it works now is merely an accident. As soon as you call another function, the stack space will be reused, corrupting the buffer content. This is guaranteed to happen when you call this code from C#, the P/Invoke marshaller implementation will overwrite the stack frame and corrupt the buffer.

To make matters worse, the P/Invoke marshaller will try to free the buffer. It will assume that the buffer was allocated by CoTaskMemAlloc() and call CoTaskMemFree() to release the buffer. That will silently fail on Windows XP and earlier but crash your program on Vista and up.

Which is one solution to your issue, use CoTaskMemAlloc() to allocate the buffer instead of using a local variable. The all-around better solution is to let the caller of the function pass the buffer, to be filled with the string. That way, the caller can decide how to allocate the buffer and clean it up as necessary. The function signature should look like this:

extern "C" __declspec(dllexport)
void __stdcall GetMessage(int id, char* buffer, size_t bufferSize);

Use strcpy_s() to safely fill the buffer and avoid any buffer overflow. The corresponding C# declaration would then be:

[DllImport("mydll.dll")]
private static extern void GetMessage(int id, StringBuilder buffer, int bufferSize);

and called like this:

var sb = new StringBuilder(250);
GetMessage(id, sb, sb.Capacity);
string retval = sb.ToString();
Hans Passant