2009-06-10 12 views
3

Ich habe es mit einer Reihe nativer Funktionen zu tun, die Daten über dynamisch zugewiesene Arrays zurückgeben. Die Funktionen nehmen einen Referenzzeiger als Eingabe und zeigen ihn dann auf das resultierende Array.C#: Unmanaged Array in eine verwaltete Liste konvertieren

Zum Beispiel:

typedef struct result 
{ 
    //..Some Members..// 
} 

int extern WINAPI getInfo(result**); 

Nach dem Aufruf 'Ergebnis' zeigt auf einen null-terminierte Array von Ergebnis *.

Ich möchte eine verwaltete Liste aus diesem nicht verwalteten Array erstellen. Ich kann folgendes tun:

struct Result 
{ 
    //..The Same Members..// 
} 

public static unsafe List<Result> getManagedResultList(Result** unmanagedArray) 
{ 
    List<Result> resultList = new List<Result>(); 

    while (*unmanagedArray != null) 
    { 
     resultList.Add(**unmanagedArray); 
     ++unmanaged; 
    } 
    return result; 
} 

Dies funktioniert, wird es langweilig und hässlich sein für jede Art von Struktur neu zu implementieren, die ich zu tun haben würde mit (~ 35). Ich möchte eine Lösung, die generisch über den Typ der Struktur im Array ist. Zu diesem Zweck habe ich versucht:

public static unsafe List<T> unmanagedArrToList<T>(T** unmanagedArray) 
{ 
    List<T> result = new List<T>(); 
    while (*unmanagedArray != null) 
    { 
     result.Add((**unmanagedArray)); 
     ++unmanagedArray; 
    } 
    return result; 
} 

Aber das wird nicht kompilieren, weil Sie nicht „die Adresse nehmen, erhalten die Größe, oder zu erklären, einen Zeiger auf einen verwalteten Typen (‚T‘)“.

Ich habe auch versucht, dies ohne unsafe Code zu tun, aber ich lief auf das Problem, dass Marshal.Copy() die Größe des nicht verwalteten Array kennen muss. Ich konnte dies nur mit unsicherem Code feststellen, daher schien es keinen Vorteil zu geben, in diesem Fall Marshal.Copy() zu verwenden.

Was fehlt mir? Könnte jemand einen generischen Ansatz für dieses Problem vorschlagen?

Antwort

2

Sie können eine vernünftige Annahme treffen, dass Größe und Darstellung aller Zeiger gleich sind (nicht sicher, ob C# -Spezifikation dies garantiert) , aber in der Praxis werden Sie feststellen, dass dies der Fall ist). So können Sie Ihre T** als IntPtr* behandeln. Ich sehe auch nicht, wie Marshal.Copy Ihnen hier helfen würde, da es nur Überladungen für eingebaute Typen hat. Also:

public static unsafe List<T> unmanagedArrToList<T>(IntPtr* p) 
{ 
    List<T> result = new List<T>(); 
    for (; *p != null; ++p) 
    { 
     T item = (T)Marshal.PtrToStructure(*p, typeof(T)); 
     result.Add(item); 
    } 
    return result; 
} 

Natürlich werden Sie eine explizite Umwandlung zu IntPtr* benötigen, wenn Sie dies nennen, aber zumindest gibt es keine Code-Duplizierung anders.

0

Sie sagte:

Marshal.Copy(), um die Größe des nicht verwalteten Array wissen muss. Ich konnte nur dies mit unsicheren Code bestimmen

Es scheint, dass Sie Marshal.SizeOf() sind vermisst.

Von dem, was Sie in der Post erwähnt haben, kann das ausreichen, um Ihr Problem zu lösen. (Außerdem muss der Parameter Ihrer Funktion möglicherweise Objekt ** anstelle von T ** sein.)

+0

Marshal.Copy muss wissen, wie viele Elemente sich im Quell-Array befinden, aber das weiß ich zur Kompilierzeit nicht, da das Array dynamisch vom systemeigenen Code zugewiesen wird. Ich kenne keine Möglichkeit, die Anzahl der Elemente im nicht verwalteten Array zu ermitteln, ohne unsicheren Code zu verwenden. – Odrade

+0

Objekt ist auch ein verwalteter Typ. Wie oben erwähnt, können Sie keine unsicheren Operationen für einen verwalteten Typ ausführen. – Odrade

+0

Im schlimmsten Fall können Sie einen IntPtr für den Parameter Ihrer Funktion verwenden und dann Marshal.Copy() einmal für jedes Element im Array aufrufen, bis Sie das Ende erreicht haben. Sie sollten in der Lage sein, dies zu verkürzen, indem Sie eine Kopie des übergebenen IntPtr mit Marshal erstellen.SizeOf(), um die richtige Größe zu bestimmen, als diese Größe in einer Schleife hinzuzufügen, bis Sie den Nulleintrag gefunden haben. –