2010-12-17 11 views
3

und danke im Rat für jede Hilfe.Problem mit Interop C#/C: AccessViolationException

i haben diese triviale Funktion in C:

__declspec(dllexport) Point* createPoint (int x, int y) { 
    Point *p; 

    p = (Point*) malloc(sizeof(Point)); 
    p->x = x; 
    p->y=y; 

    return p;  
} 

Punkt ist eine sehr einfache Struktur mit zwei Feldern int, x und y.

Ich möchte diese Funktion von C# aufrufen.

Ich benutze diesen Code:

[DllImport("simpleC.dll", EntryPoint = "createPoint", CallingConvention = CallingConvention.Cdecl, SetLastError = true, CharSet = CharSet.Auto)] 
[return: MarshalAs(UnmanagedType.LPStruct)] 
public static extern Point createPoint(int x, int y); 

Point p = Wrapper.createPoint(1, 2); 

aber zur Laufzeit habe ich eine AccessViolationException. Beobachten Ausnahme im Detail, fand ich, dass Ausnahme von Marshal.CoTaskMemFree(IntPtr) Methode geworfen wird.

Es scheint, dass diese Methode den von C malloc zugewiesenen Speicher nicht freigeben kann.

Was mache ich falsch?

Wirklich danke.

Antwort

2

CoTaskMemFree kann nicht zum Freigeben von Speicher verwendet werden, der von malloc zugewiesen wurde (weil sie unterschiedliche Zuordner verwenden). Laut MSDN "Die Laufzeit verwendet immer die CoTaskMemFree-Methode, um Speicher freizugeben. Wenn der Speicher, mit dem Sie arbeiten, nicht mit der CoTaskMemAlloc -Methode zugewiesen wurde, müssen Sie einen IntPtr verwenden und den Speicher manuell mit der entsprechenden Methode freigeben."

Zusätzlich Adam Nathan notes, dass "UnmanagedType.LPStruct wird nur für einen bestimmten Fall unterstützt: Behandeln eines System.Guid-Werttyps als eine nicht verwaltete GUID mit einer zusätzlichen Ebene der Indirektion. ... Sie sollten wahrscheinlich bleiben weg von UnmanagedType. LPStruct."

Es gibt zwei mögliche Lösungen:

  1. Deklarieren Sie den Rückgabetyp der Methode als IntPtr und verwenden Marshal.ReadInt32 die Felder der Struktur zu lesen, oder verwenden Sie Marshal.PtrToStructure die Daten zu einer verwalteten Struktur zu kopieren, oder verwenden unsicheren Code den IntPtr Wert auf ein Point * zu werfen. die C-Bibliothek benötigt ein destroyPoint(Point *) Verfahren auszusetzen, die den Speicher freigibt.
  2. ändern der C-Methode Signatur void getPoint(int x, int y, Point *). auf diese Weise kann C#, um die Struktur zuweisen, und die C-Methode einfach füllt die Datenwerte. (Die meisten Win32 APIs sind auf diese Weise definiert).

Eine letzte Anmerkung: Es sei denn, Ihre Methode, um das SetLastError Win32-API verwendet, brauchen Sie nicht SetLastError = true auf Ihrem P/Invoke-Attribute angeben.

+0

Dank vielen, klären Sie mich irgendwelche denken! Mein eigentliches Ziel ist die Integration von pcre C library für regulären Ausdruck in C#, dann gibt es eine Menge o malloc und sruct pointer. Jetzt benutze ich zweite Lösung, es funktioniert, aber es ist hart freier Speicher (ich sollte C Funktionen deallocator schreiben), aber ich mag die zweite, und werde es implementieren! Danke vielmals! –

1

Da Sie nicht den Code haben, der "p" freigibt, ist es schwer zu sagen. Es ist jedoch wahrscheinlich, dass die Art, wie malloc() und free() zusammenarbeiten, völlig anders ist als die Art, wie C# den Speicher verwaltet. Da C# Garbage Collection hat (glaube ich), ist es wahrscheinlich, dass es ein völlig anderes Speicherverwaltungssystem verwendet.

In jedem Fall ist die richtige Lösung, wenn Sie Ihre Bibliothek verwenden, um ein Objekt zu erstellen, sollten Sie es auch verwenden, um es zu zerstören. Implementieren Sie eine "destroyPoint" -Funktion, die den Speicher in Ihrer C-Bibliothek freigibt, in den C# -Code importiert und von dort aus aufruft, um die von Ihrer C-Bibliothek erstellten Objekte zu zerstören.

Als allgemeine Design-/Codierungsregel sollte jede "create" -Funktion eine übereinstimmende "free/destroy/delete" -Funktion haben. Abgesehen von nichts anderem, macht es leicht sicherzustellen, dass alle erstellten Objekte ordnungsgemäß zerstört werden.

+0

vielen Dank :) akk Sie geben mir die Bestätigung, dass ein Objekt aus C zugewiesen werden kann, kann nur aus einer anderen C-Funktion freigegeben werden! –

1

Wie wird der Punkttyp auf der C# -Seite definiert?
Es muss unsicher sein, oder Sie müssen einen ungültigen Zeiger (IntPtr) zurückgeben. Der GC kann keine Referenzen von außen (hier der zugewiesene Speicher) zählen, daher kann Ihr Code nicht erwarten, extern zugewiesenen Speicher über den GC zu verwalten.
Eine Alternative besteht darin, eine statische Referenz zu behalten, um eine Garbage-Collection zu vermeiden, wenn Sie das Objekt während der Laufzeit Ihrer Anwendung persistent halten müssen.

+0

Hallo, danke für deine Answare ... im Effekt gebe ich Zeiger für jetzt zurück, dann in C# mache ich korrekte Besetzung. Problem ist, dass der Speicher, auf den der Zeiger verweist, nicht zuweisbar ist, es sei denn, eine extra C-Funktion –