There are several flaws as pointed out in the various replies you've got. To summarize it all, here is what I'd suggest.
On the native side, declare something like this (assuming you are coding in C++):
extern "C"
__declspec(dllexport)
const char* __cdecl Connect(const char* lpPostData)
{
static char buffer[1000];
...
return buffer;
}
The extern "C"
makes it clear to the compiler that it should not mangle the function name when it gets exported, but should treat it just as it would treat a C function.
The __ceclspec(dllexport)
makes the function visible in the DLL as an exported entry point. You can then reference it from outside projects.
The __cdecl
ensures that you use the C way of passing arguments on the stack. You could badly mess things up if the caller does not assume the same argument passing scheme.
Use char
consistently: either sick to char
(ANSI text) or wchar_t
(16-bit Unicode text). And never, ever, return a pointer to a local variable, as you did in your example. As soon as the function returns, that memory location won't be valid any more and could contain garbage. Furthermore, if the user is modifying the data stored there, it can cause the program to crash, because this would trash the call stack.
On the managed C# side, here is what I recommend:
[DllImport("test.dll", EntryPoint="Connect",
CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
private static extern System.IntPtr Connect(string postData);
which will bind to your Connect
entry point, use the ANSI string convention (8-bit per character, which is what char
expects) and make sure the caller is expecting the __cdecl
calling convetion.
You'll have to marshal the IntPtr
to a string
object by calling:
string value = Marshal.PtrToStringAnsi (Connect (postData));
Note: in my original post, I recommended declaring Connect
as returning string
, but as commenter Mattias corrected, this is not the proper way of doing it. See this article on CLR Inside Out to understand how the CLR is treating retval
s of type string
. Thanks for pointing this out to me (and thanks to romkyns too).
You could also consider copying the string from buffer
to a piece of memory allocated through CoTaskMemAlloc
in the native code, and returning a pointer to it. You could then simply declare the DllImport
function as returning string
and would not need to do any further marshalling in the managed world.