2011-12-25 16 views
7

Necesito devolver una lista de puntos que tengo de una aplicación C dll a C# utilizando PInvoke. Estos son puntos en 3 dimensiones [x, y, z]. La cantidad de puntos varía según el tipo de modelo. En C manejo esta una lista de estructuras enlazadas. Pero no veo cómo puedo pasar esto a C#.Lista de devolución de puntos (x, y, z) de C a C# utilizando PInvoke

De la forma en que lo veo, tengo que devolver una matriz bidimensional flexible, probablemente en una estructura.

¿Alguna sugerencia de cómo se puede hacer esto? Ambas ideas sobre cómo devolverlo en C y cómo acceder a él en C# son muy apreciadas.

Respuesta

5

una lista de estructuras podría se pasa de nuevo unida, pero sería bastante complicado de tratar, ya que tendría que escribir código para recorrer los punteros, la lectura y la copia de los datos de la memoria nativa en gestionados espacio de memoria Yo recomendaría una simple serie de estructuras en su lugar.

Si usted tiene una estructura como la siguiente C (asumiendo enteros de 32 bits) ...

struct Point 
{ 
    int x; 
    int y; 
    int z; 
} 

... entonces lo que representa casi la misma forma en C#:

[StructLayout(LayoutKind.Sequential] 
struct Point 
{ 
    public int x; 
    public int y; 
    public int z; 
} 

Ahora, para pasar una matriz hacia atrás, sería más fácil hacer que su código nativo asigne la matriz y la devuelva como un puntero, junto con otro puntero que especifique el tamaño en los elementos.

Su prototipo C podría tener este aspecto:

// Return value would represent an error code 
// (in case something goes wrong or the caller 
// passes some invalid pointer, e.g. a NULL). 
// Caller must pass in a valid pointer-to-pointer to 
// capture the array and a pointer to capture the size 
// in elements. 
int GetPoints(Point ** array, int * arraySizeInElements); 

El P/Invoke declaración sería entonces la siguiente:

[DllImport("YourLib.dll")] 
static extern int GetPoints(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out Point[] array, 
    out int arraySizeInElements); 

El atributo MarshalAs especifica que la matriz se debe calcular las referencias utilizando el tamaño especificado en el segundo parámetro (puede leer más sobre esto en MSDN, "Default Marshaling for Arrays").

Si utiliza este método, tenga en cuenta que imprescindible el uso CoTaskMemAlloc para asignar el búfer nativo ya que esto es lo que espera el contador de referencias .NET. De lo contrario, obtendrá fugas de memoria u otros errores en su aplicación.

Aquí hay un fragmento de la simple ejemplo compilé al verificar mi respuesta:

struct Point 
{ 
    int x; 
    int y; 
    int z; 
}; 

extern "C" 
int GetPoints(Point ** array, int * arraySizeInElements) 
{ 
    // Always return 3 items for this simple example. 
    *arraySizeInElements = 3; 

    // MUST use CoTaskMemAlloc to allocate (from ole32.dll) 
    int bytesToAlloc = sizeof(Point) * (*arraySizeInElements); 
    Point * a = static_cast<Point *>(CoTaskMemAlloc(bytesToAlloc)); 
    *array = a; 

    Point p1 = { 1, 2, 3 }; 
    a[0] = p1; 

    Point p2 = { 4, 5, 6 }; 
    a[1] = p2; 

    Point p3 = { 7, 8, 9 }; 
    a[2] = p3; 

    return 0; 
} 

La persona que llama logrado luego pueden hacer frente a los datos de forma muy simple (en este ejemplo, puse todo el código de interoperabilidad dentro de una clase estática llamada NativeMethods):

NativeMethods.Point[] points; 
int size; 
int result = NativeMethods.GetPoints(out points, out size); 
if (result == 0) 
{ 
    Console.WriteLine("{0} points returned.", size); 
    foreach (NativeMethods.Point point in points) 
    { 
     Console.WriteLine("({0}, {1}, {2})", point.x, point.y, point.z); 
    } 
} 
+0

Esto parece funcionar excelente. No sabía sobre CoTAskMemAlloc. Gracias – user978281

Cuestiones relacionadas