2009-08-21 15 views
16

Ich habe die folgende C++ Funktionsdefinition, die ich durch PInvoke von verwalteten Code aufzurufen bin versucht:Richtiger Weg, um SIZE_T * zu marshalieren?

bool FooBar(SIZE_T* arg1); 

Meine verwaltete Erklärung sah wie folgt aus:

[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)] 
private static extern bool FooBar(ref uint arg1); 

Einige von euch das gleiche feststellen kann Bug habe ich schließlich getan. Dies ist nicht 64-Bit-Portable. SIZE_T hat eine variable Größe (32-64 Bit) wie der Zeiger darauf. Bei der verwalteten Größe wird der Zeiger korrekt in 64-Bit übersetzt, aber die Uint nicht, und Sie können mit Müll in den oberen Bits von arg1 enden. Dies war ein besonders hartnäckiger Fehler, da der Müll oft nur Nullen :(

Die einzige Lösung, die ich an der Arbeit bekommen habe, ist die folgende verwaltete Deklaration:

[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)] 
private static extern bool FooBar(ref IntPtr arg1); 

Dies funktioniert natürlich, weil IntPtr seine Größe variieren kann In meinem Code behandle ich IntPtr einfach als Ganzzahl, und es funktioniert, obwohl es wie ein hässlicher Hack aussieht. Es scheint mir, dass es eine Möglichkeit geben sollte, dies richtig zu spezifizieren, vielleicht mit UnmanagedType.SysUInt, aber ich war es nicht in der Lage, mit einer anderen Arbeitslösung zu arbeiten

Antwort

19

Mit IntPtr und/oder UIntPtrist macht es richtig - die Typen gibt es speziell für diesen Zweck! Ich verstehe nicht, warum Sie es für einen "hässlichen Hack" halten. Ich bin mir auch nicht sicher, was Ihre vorgeschlagene Alternative wäre - jede Art von Attributen, die Werte zu uint zugeordnet werden können, wäre von Natur aus falsch, weil C# uint garantiert 32-Bit unabhängig von der Architektur ist, und so weiter 64- Bit-Plattform, um es richtig zu marshalieren, müsste es die Hälfte davon abschneiden, Daten verlieren und das Ergebnis wahrscheinlich nutzlos machen.

+1

Ich sehe es als ein hässlicher Hack, weil IntPtr einen Zeiger auf etwas darstellen sollte. Ich behandle es einfach direkt als Integer. SysUInt gibt ausdrücklich an, dass es sich um eine Darstellung variabler Größe einer Uint handelt. Genau das, was ich will, außer dass ich nicht herausfinden kann, wie man es als SysUInt-Pointer, anstatt als SysUInt, richtig einrichtet. – sooniln

+0

Sie verwenden einen Zeiger auf SIZE_T als Argument, Soonil. IntPtr sieht sehr passend aus –

+0

Sie werden bemerken, dass ich IntPtr nicht verwende, aber ich verwende ref IntPtr. – sooniln

12

UIntPtr ist der richtige zu verwendende Typ.

size_t ist eine vorzeichenlose, zeigergroße ganze Zahl und genau das bedeutet UIntPtr. Das "ptr" im Namen kann ein wenig verwirrend sein, stimme ich zu. Es bedeutet nicht "Dies ist ein Zeiger", es bedeutet "Dies ist ein Zeiger-Größe Integer". Ihre Deklaration wäre also:

[DllImport("mydll", SetLastError=true, CharSet=CharSet.Unicode)] 
private static extern bool FooBar(ref UIntPtr arg1); 
+0

Ah sehr gut dann :) Wäre nicht die erste schlecht benannte API, heh. – sooniln

+0

Eigentlich ist das eine Art allgemeiner Name für diese Art von Art. Win32 APIs hat typedefs 'INT_PTR' und' UINT_PTR'. Und ISO C99 hat 'intptr_t' und' uintptr_t' in ''. Obwohl es vielleicht eine falsche Bezeichnung ist, ist es mittlerweile eine traditionelle. –

Verwandte Themen