2013-07-16 3 views
7

Dies funktioniert:Wie marschiere ich zu ANSI-Zeichenfolge über Attribut?

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

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

Diese Abstürze:

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

This article schlägt vor, dass die Rückkehr Attribut ist im Wesentlichen wie Marshal.PtrToStringAnsi nennen, so was ist der Deal?


Als Daniel pointed out, ist es wahrscheinlich abstürzt, weil der Einweiser den Speicher zu frei versucht. Der Artikel gibt auch an:

N.B. Beachten Sie, dass die nicht verwaltete Seite das Schlüsselwort "new" oder die C-Funktion "malloc()" nicht verwenden darf, um Speicher zuzuweisen. Der Interop Marshaler kann den Speicher in diesen Situationen nicht freigeben. Dies liegt daran, dass das Schlüsselwort "new" vom Compiler und die Funktion "malloc" von der C-Bibliothek abhängig ist.

Ich habe versucht, den char-Zeiger mit Marshal.FreeHGlobal, Marshal.FreeCoTaskMem und Marshal.FreeBSTR befreien - sie alle zum Absturz bringen. Es gibt keine anderen Möglichkeiten, um den Speicher AFAIK zu befreien, also nehme ich an, dass der Speicher über new oder malloc() zugewiesen wurde. Also, was jetzt, ich bin eingesperrt? Ich habe ein permanentes Speicherleck in meinem Programm?

Ich überprüfte die Quelle. Die Zeichenfolge wird über static char errmsg[SDL_ERRBUFIZE] erstellt. Mein C ist rostig, aber ich denke, es ist als static deklariert, so dass es nicht freigegeben wird, wenn es den Funktionsumfang verlässt. Ich kann mich nicht erinnern, wo statische Arrays im Speicher landen. Gibt es einen Weg, sie zu befreien?

Bearbeiten: Warten ... es ist statisch. Das heißt, jedes Mal, wenn ein neuer Fehler auftritt, wird die alte Fehlermeldung überschrieben, weshalb SDL_GetError() nur die letzte Fehlermeldung zurückgibt. Ergo, ich muss mich nicht darum kümmern, es zu befreien.

Als solche, wenn alle return: MarshalAs... Optionen versuchen, den Speicher freizugeben, dann ist die einzige Lösung meine aktuelle. Dies ist schließlich optimal.

+0

Ja, wenn es statisch ist, darf der Speicher nicht freigegeben werden. Die einzige Möglichkeit besteht darin, manuell zu marshallen, da sonst der CLR-Marshaller den Speicher freigibt. –

Antwort

3

Ich habe etwas graben. Die Quelle für SDL_GetError ist:

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

    return SDL_GetErrorMsg(errmsg, SDL_ERRBUFIZE); 
} 

wir, dass der Speicher für die Zeichenfolge sehen kann als static char Array zugeordnet ist. Es wird jedes Mal überschrieben, wenn SDL_GetError aufgerufen wird. Als solche können und müssen wir es nicht befreien.

Da die [return: MarshalAs.*] Methoden alle versuchen, Speicher nach dem Marshalling des Typs zu befreien, werden sie nicht funktionieren (und weiterhin dazu führen, dass das Programm abstürzt).

Als solche ist Ihre (meine) ursprüngliche Lösung optimal.

4

Wie im verknüpften Artikel angegeben, wird der Speicher der systemeigenen Zeichenfolge bei Verwendung von [return: MarshalAs(UnmanagedType.LPStr)] mithilfe der CLR mithilfe von FreeCoTaskMem() freigegeben. Wenn Sie das verwaltete String-Objekt manuell über Marshal.PtrToStringAnsi() erstellen, wird der Speicher nicht freigegeben.

Wenn es abstürzt, dann wurde der String wahrscheinlich nicht auf der nicht verwalteten Seite über CoTaskMemAlloc() erstellt, sondern über new() oder malloc() (zum Beispiel). Die API von SDL_GetError() sollte angeben, wessen Aufgabe es ist, die native Zeichenfolge und wie zu befreien.