2013-07-16 11 views
7

Questo funziona:Come eseguire il marshalling sulla stringa ANSI tramite l'attributo?

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")] 
private static extern IntPtr SDL_GetError(); 

public static string GetError() 
{ 
    return Marshal.PtrToStringAnsi(SDL_GetError()); 
} 

Questo crash:

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")] 
[return: MarshalAs(UnmanagedType.LPStr)] 
public static extern string GetError(); 

This article suggerisce che l'attributo di ritorno è essenzialmente come chiamare Marshal.PtrToStringAnsi, quindi qual è il problema?


Come Daniel pointed out, è probabilmente perché il crash marshaller sta tentando di liberare la memoria. L'articolo afferma anche,

N.B. Nota: il lato non gestito non deve utilizzare la parola chiave "new" o la funzione "malloc()" C per allocare memoria. L'Interop Marshaler non sarà in grado di liberare la memoria in queste situazioni. Questo perché la "nuova" parola chiave dipende dal compilatore e la funzione "malloc" dipende dalla libreria C.

Ho provato liberare il puntatore char con Marshal.FreeHGlobal, Marshal.FreeCoTaskMem e Marshal.FreeBSTR - tutti crash. Non ci sono altri modi per liberare la memoria AFAIK, quindi suppongo che la memoria sia stata allocata tramite new o malloc(). Quindi, adesso, sono intrappolato? Ho una perdita di memoria permanente nel mio programma?

Ho controllato la fonte. La stringa viene creata tramite static char errmsg[SDL_ERRBUFIZE]. La mia C è arrugginita, ma suppongo che sia dichiarata come static in modo che non venga liberata quando esce dall'ambito della funzione. Non ricordo dove gli array statici vivono in memoria-terra però; c'è un modo per liberarli?

Modifica: Attendi ... è statico. Ciò significa che ogni volta che c'è un nuovo errore sovrascriverà il vecchio messaggio di errore, quindi perché SDL_GetError() restituisce solo il messaggio di errore più recente. Ergo, non devo preoccuparmi di liberarlo.

In questo modo, se tutte le opzioni return: MarshalAs... tentano di liberare la memoria, l'unica soluzione è la mia attuale. Questo è ottimale dopo tutto.

+0

Sì, se è statico, la memoria non deve essere liberata. Quindi l'unico modo è quello di effettuare il marshalling manuale, altrimenti il ​​marshaller CLR libererà la memoria. –

risposta

3

Ho fatto un po 'di scavo. La fonte per SDL_GetError è:

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

    return SDL_GetErrorMsg(errmsg, SDL_ERRBUFIZE); 
} 

Possiamo vedere che la memoria per la stringa è assegnato come array char statico. Viene sovrascritto ogni volta che viene chiamato SDL_GetError. Come tale non possiamo e non abbiamo bisogno di liberarlo.

Poiché i metodi [return: MarshalAs.*] tentano di liberare memoria dopo il marshalling del tipo, non funzioneranno (e causeranno inoltre il blocco del programma).

Come tale, la (mia) soluzione originale è ottimale.

4

Come indicato nell'articolo collegato, quando si utilizza [return: MarshalAs(UnmanagedType.LPStr)], la memoria della stringa nativa viene liberata dal CLR utilizzando FreeCoTaskMem(). Se si crea manualmente l'oggetto stringa gestita tramite Marshal.PtrToStringAnsi(), la memoria non viene affatto liberata.

Se si blocca, probabilmente la stringa non è stata creata sul lato non gestito tramite CoTaskMemAlloc(), ma tramite new() o malloc() (ad esempio). L'API di SDL_GetError() dovrebbe indicare il cui compito è liberare la stringa nativa e come.

Problemi correlati