How to marshal to ANSI string via attribute?

c, c#, marshalling

Solution

I did some digging. The source for `SDL_GetError` is:

const char *
SDL_GetError(void)
{
    static char errmsg[SDL_ERRBUFIZE];

    return SDL_GetErrorMsg(errmsg, SDL_ERRBUFIZE);
}

We can see that the memory for the string is allocated as a static char array. It is overwritten every time `SDL_GetError` is called. As such we can't and don't need to free it.

Since the `[return: MarshalAs.*]` methods all attempt to free memory after marshalling the type, they will not work (and further cause the program to crash).

As such, your (my) original solution is optimal.

Problem

This works: ``` [DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")] private static extern IntPtr SDL_GetError(); public static string GetError() { return Marshal.PtrToStringAnsi(SDL_GetError()); } ``` This crashes: ``` [DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string GetError(); ``` This article suggests that the return attribute is essentially like calling `Marshal.PtrToStringAnsi`, so what's the deal? As Daniel pointed out, it's probably crashing because the marshaller is attempting to free the memory. The article also states, N.B. : Note that the unmanaged side must not use the “new” keyword or the “malloc()” C function to allocate memory. The Interop Marshaler will not be able to free the memory in these situations. This is because the “new” keyword is compiler dependent and the “malloc” function is C-library dependent. I've tried freeing the char pointer with `Marshal.FreeHGlobal`, `Marshal.FreeCoTaskMem` and `Marshal.FreeBSTR` -- they all crash. There aren't any other ways of freeing the memory AFAIK, so I'm guessing the memory was allocated via `new` or `malloc()`. So what now, I'm hooped? I have a permanent memory leak in my program? I checked the source. The string is created via `static char errmsg[SDL_ERRBUFIZE]`. My C is rusty, but I guess it's declared as `static` so that it doesn't get freed when it goes out of function scope. I don't remember where static arrays live in memory-land though; is there some way of freeing them? Edit: Wait... it's static. That means each time there's a new error it will overwrite the old error message, hence why `SDL_GetError()` only returns the most recent error message. Ergo, I don't have to worry about freeing it. As such, if all the `return: MarshalAs...` options attempt to free the memory, then the only solution is my current one. This is optimal after all.

Original source