tags:

views:

1853

answers:

4

I'm trying to write some C# code that calls a method from an unmanaged DLL. The prototype for the function in the dll is:

extern "C" __declspec(dllexport) char *foo(void);

In C#, I first used:

[DllImport(_dllLocation)]
public static extern string foo();

It seems to work on the surface, but I'm getting memory corruption errors during runtime. I think I'm pointing to memory that happens to be correct, but has already been freed.

I tried using a PInvoke code gen utility called "P/Invoke Interop Assistant". It gave me the output:

[System.Runtime.InteropServices.DLLImportAttribute(_dllLocation, EntryPoint = "foo")]
public static extern System.IntPtr foo();

Is this correct? If so, how do I convert this IntPtr to a string in C#?

+2  A: 

You can use the Marshal.PtrToStringAuto method.

IntPtr ptr = foo();
string str = Marshal.PtrToStringAuto(ptr);
Strelok
A: 

I'd be interested in knowing what the policy of the API is for managing the memory returned from function. Is it a pointer to a fixed block of memory that is always there? Are you supposed to call another API to free the memory? Are you supposed to call a WIN32 API yourself on the memory returned (e.g., by calling Marshal.FreeHGlobal or Marshal.FreeCoTaskMem)?

Of course you've simplified the API for the question but do you know what the policy is?

dpp
Yes, it's a pointer to a fixed block of memory that is always there.
Brandon
+7  A: 

You must return this as an IntPtr. Returning a System.String type from a PInvoke function requires great care. The CLR must transfer the memory from the native representation into the managed one. This is an easy and predictable operation.

The problem though comes with what to do with the native memory that was returned from foo(). The CLR assumes the following two items about a PInvoke function which directly returns the string type

  1. The native memory needs to be freed
  2. The native memory was allocated with CoTaskMemAlloc

Therefore it will marshal the string and then call CoTaskMemFree on the native memory blob. Unless you actually allocated this memory with CoTaskMemAlloc this will at best cause a crash in your application.

In order to get the correct semantics here you must return an IntPtr directly. Then use Marshal.PtrToString* in order to get to a managed String value. You may still need to free the native memory but that will dependent upon the implementation of foo.

JaredPar
+1  A: 

You can also say:

[DllImport(_dllLocation)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern string foo();

To have the char* marshalled back to a C# string

Sean
What does happen here internally?
ulrichb