Each time a variable is passed between managed and unmanaged code it is marshalled. AFAIK, for strings, a copy is passed, therefore the pointer to the string you are freeing will probably not be the one you allocated.
If it were my code, I'd try to keep all the memory management in C# (if at all possible).
The StringBuilder class has a different treatment under marchalling than a string in that it can be passed to unmanaged code, changed, and then returned.
Here's my ammended dll
And here's my C# code
#define CSHARP_DLL_API extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
CSHARP_DLL_API void get_stringsafe(char *pszStr)
public class MyDLL
public static extern void get_stringsafe(StringBuilder pszStr);
public static void Main()
StringBuilder oString = new StringBuilder();
oString.Capacity = 50;