2010-11-18 4 views
1

Kurzversion:GlobalAlloc Flaggen für Marshal.PtrToStructure

konnte den Griff von GlobalAlloc (GMEM_MOVEABLE, Größe) zu Marshal.PtrToStructure() und Marshal.FreeHGlobal() Ursache Speicherbeschädigung vorbei?

Lange Version:

Ich verwende Windows Globale Speicherzuweisung eine Datenstruktur hin und her zwischen einem Delphi und C# Anwendung zu übergeben (die Tatsache, dass es Delphi ist auf diese Frage nicht wirklich von Bedeutung ist, weil es nur Win32-API-Aufrufe).

Auf der Delphi Seite, ich in einem Datensatz passieren, ordnet sie den Raum, sperrt den Speicher kopiert die Struktur in den Speicher und die Unlocks der Speicher:

function MarshalRec(SourceRec: TInteropItemRec): THandle; 
var 
    Size: integer; 
    Buffer: Pointer; 
begin 
    Size := sizeof(SourceRec); 
    result := GlobalAlloc(GMEM_MOVEABLE and GMEM_ZEROINIT, Size); 
    Buffer := GlobalLock(result); 
    try 
    CopyMemory(Buffer, @SourceRec, Size); 
    finally 
    GlobalUnlock(result); 
    end; 
end; 

Auf der C# Seite, es wird dass THandle in ein IntPtr (die im Grunde ein unsigned int ist) und verwendet Marshal.PtrToStructure die Daten in die C# Struktur zu kopieren:

public void FromMemory(IntPtr Source) 
{ 
    Marshal.PtrToStructure(Source, this); 
    Marshal.FreeHGlobal(Source); 
} 

Das Problem in ich laufe, ist sehr selten (wie in 4 mal über 6 Monate für mich), die ganze Anwendung geht unter (Diese Anwendung hat de konterte einen Fehler und muss schließen). Wenn ich versuche, die Ausführung in Visual Studio anzuhalten, erhalte ich "Es ist ein schwerwiegender Fehler aufgetreten und das Debugging muss beendet werden. Weitere Informationen finden Sie auf der Microsoft Hilfe- und Support-Website. HRESULT = 0x80131c08."

Wie auch immer, es gelang uns, ein paar Protokolle davon zu erhalten, und in beiden Fällen zeigte es einen kürzlichen Aufruf dieser "MarshalRec" -Funktion oben, ein paar andere Funktionsaufrufe und dann einige Verarbeitung von Windows-Nachrichten auf dem Event-Schleife auf dem Delphi-Thread (ja, es hat seinen eigenen Thread und Event-Schleife, um mit einem zeitempfindlichen Gerätetreiber umzugehen).

Also mein Verdacht fällt auf das GMEM_MOVEABLE Flag zu GlobalAlloc. Ich konnte nichts in der Marshal-Klasse finden, die die GlobalLock- und GlobalUnlock-Sachen ausführt, also hatte ich angenommen, dass es intern von PtrToStructure() gehandhabt wurde.

Behandelt PtrToStructure ordnungsgemäß mit einem Punkt, oder benötigt es einen tatsächlichen Zeiger, der von GlobalLock() abgerufen wird? Ist es möglich, dass Windows in seltenen Fällen den zugewiesenen Speicher verschiebt, was bedeutet, dass ich GlobalLock() aufrufen muss, um einen tatsächlichen Zeiger zu erhalten? Dass FreeHGlobal tatsächlich etwas freigibt, das es nicht haben sollte, was beim nächsten Zugriff auf die Ressource die gesamte App zum Absturz bringt?

Und wenn ja, sollte das Ändern von GMEM_MOVABLE zu GMEM_FIXED verhindern, dass dies wieder geschieht? Oder brauche ich DllImport GlobalLock() und GlobalUnlock()?

Ich bin versucht, blind diese Änderungen zu machen, aber angesichts der Nicht-Reproduzierbarkeit dieses Problems, gibt es keine Möglichkeit zu sagen, ob es behoben ist, bis es wieder passiert. Ich bin auf der Suche nach Feedback, ob dieser Code zu den Symptomen führen könnte, die ich sehe, oder ob ich andere Theorien entwickeln muss.

Antwort

2

Nun, Sie verletzen explizit den Vertrag für GlobalAlloc(). Ihre Hoffnung, dass Marshal.PtrToStructure() GlobalLock aufrufen wird, ist unbegründet, es gibt keine Möglichkeit zu sagen, ob das übergebene IntPtr ein Handle oder ein Zeiger ist.

GlobalAlloc ist eine ziemlich hoffnungslos veraltete Legacy-Funktion aus der Windows-3.x-Ära. Ja, es ist sehr wahrscheinlich, dass die Adresse als Handle-Wert zurückgegeben wird, wobei GlobalLock() ein No-Op ist. Aber es ist sicherlich nicht dokumentiert, dies zu tun. CoTaskMemAlloc() ist die bessere Mausefalle.

+0

Danke für die Bestätigung. Basierend auf dem Beispielcode, den ich darauf aufbaue, stelle ich mir vor, dass es ein paar andere Anwendungen gibt, die es auch falsch machen. Ich wünschte, der XML-Kommentar für Marshal.AllocHGlobal machte deutlich, dass CoTaskMem angesichts der Wahl dem HGlobal vorgezogen wird. –