2011-01-04 14 views
9

ich mit Code bin eine Schnittstelle, die eine char** nimmt (das heißt, ein Zeiger auf eine Zeichenfolge):Rangierung ein char ** in C#

int DoSomething(Whatever* handle, char** error); 

Im Grunde ist es ein Handle auf seinen Zustand annimmt, und wenn etwas schief geht, es gibt einen Fehlercode und gegebenenfalls eine Fehlermeldung (der Speicher extern zugeordnet ist und mit einer zweiten Funktion befreit. der Teil I figued out haben :)).

Ich aber bin nicht sicher, wie in C# zu handhaben in. Was ich habe, zur Zeit:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(IntPtr handle, byte** error); 

public static unsafe int DoSomething(IntPtr handle, out string error) { 
    byte* buff; 

    int ret = DoSomething(handle, &buff); 

    if(buff != 0) { 
     // ??? 
    } else { 
     error = ""; 
    } 

    return ret; 
} 

Ich habe um stocherte, aber ich kann nicht herausfinden, wie man das sich in eine byte[], geeignet ist, auf dem richtigen Weg zu UTF8Encoding.UTF8.GetString()

Bin ich füttern?

EDIT: Um noch deutlicher zu machen, die Bibliotheksfunktion reserviert Speicher, die durch den Aufruf eine andere Bibliotheksfunktion befreit werden müssen. Wenn eine Lösung mich nicht mit einem Zeiger verlässt, den ich freilassen kann, ist die Lösung inakzeptabel.

Frage Bonus: Wie oben angedeutet, diese Bibliothek verwendet UTF-8 für die Saiten. Muss ich etwas Besonderes in meiner P tun/Invokes, oder einfach nur string für normale const char* Parameter verwenden?

+0

Haben Sie 'out StringBuilder' probiert? – leppie

+0

Ich habe einen zusätzlichen Schnitt hinzugefügt, der das Grundproblem erklärt. –

Antwort

2

Sie sollten nur in der Lage sein, eine ref string zu verwenden und den Runtime Default Marshaller kümmern sich um diese Konvertierung für Sie. Sie können mit [MarshalAs(UnmanagedType.LPStr)] die char Breite auf dem Parameter andeuten, um sicherzustellen, dass Sie 8-Bit-Zeichen verwenden.

Da Sie eine spezielle Methode zum Aufheben der Zuweisung haben, müssen Sie den Zeiger beibehalten, wie Sie bereits im Beispiel Ihrer Frage gezeigt haben.

Hier ist, wie ich es schreiben würde:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(
    MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy 

Dann können Sie eine Zeichenfolge erhalten:

var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error)); 

und Cleanup:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern int FreeMyMemory(IntPtr h); 

// ... 

FreeMyMemory(new IntPtr(error)); 

Und jetzt haben wir die vermarshallten Fehler , also gib es einfach zurück.

return errorMsg; 

Beachten Sie auch die MySafeHandle-Typ, der von System.Runtime.InteropServices.SafeHandle erben würde. Obwohl dies nicht unbedingt erforderlich ist (Sie können IntPtr verwenden), bietet es Ihnen ein besseres Handle-Management, wenn Sie mit nativem Code interoperieren. Lesen Sie hier darüber nach: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx.

+0

Wie gehe ich mit dem Speicher um? Um es noch einmal zu sagen: 'DoSomething' wird Speicher reservieren und in' * error' speichern. Ich muss dieses Gedächtnis befreien ... –

+0

Sie sollten wissen, wie es zugeteilt wird: globaler Haufen oder lokaler? – codekaizen

+0

Es ist Heap, aber von der Bibliothek zugewiesen. Ich muss 'FreeMyMemory' in' mydll.dll' aufrufen, um es freizugeben. Ich kann die Adresse nicht von einer 'ref string' bekommen, um das zu tun. –

0

Als Referenz hier ist Code, der kompiliert ( aber noch nicht getestet, auf den nächsten Arbeits getestet, funktioniert 100%), das tut, was ich brauche. Wenn jemand besser machen kann, das ist, was ich nach: D

public static unsafe int DoSomething(IntPtr handle, out string error) { 
    byte* buff; 

    int ret = DoSomething(handle, &buff); 

    if(buff != null) { 
     int i = 0; 

     //count the number of bytes in the error message 
     while (buff[++i] != 0) ; 

     //allocate a managed array to store the data 
     byte[] tmp = new byte[i]; 

     //(Marshal only works with IntPtrs) 
     IntPtr errPtr = new IntPtr(buff); 

     //copy the unmanaged array over 
     Marshal.Copy(buff, tmp, 0, i); 

     //get the string from the managed array 
     error = UTF8Encoding.UTF8.GetString(buff); 

     //free the unmanaged array 
     //omitted, since it's not important 

     //take a shot of whiskey 
    } else { 
     error = ""; 
    } 

    return ret; 
} 

Edit: die Logik in der while Schleife befestigt ist, hatte es ein Off von einem Fehler.