2012-06-21 13 views
5

Mis C declaraciones son las siguientes:¿Cómo identifico un puntero a un puntero de una matriz de estructuras?

int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data); 

typedef struct { 
    byte Rel; 
    __int64 Time; 
    char Validated; 
    unsigned char Data[1]; 
} DATASTRUCT ; 

Mi C# declaraciones son las siguientes:

[DllImport("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data); 

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public struct DATASTRUCT 
{ 
    public sbyte Rel; 
    public long Time; 
    public byte Validated; 
    public double Data; 
} 

entonces llamar a la función administrada de la siguiente manera:

string dataToShow = "description"; 
long Time; 
uint maxData; // How many structs will be returned, i.e. how much data is available 
uint myHandle = 1; 

DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // doesn't matter what I specify as the array size? 

myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation); 

Tras la ejecución de lo anterior la función regresará con éxito con solo una estructura aunque haya 3 para regresar. ¿Por qué esto es tan?

Información adicional; He tratado de pasar el puntero a un puntero de una serie de estructuras de las siguientes maneras:

- ref DATASTRUCT[] data; // Works but only returns one struct 
- [Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage 

Según entiendo que podría necesitar hacer un poco de clasificación manual utilizando IntPtr, no sé cómo implementar esto, sin embargo, entonces cualquier consejo sería apreciado.

+0

¿Cómo está asignando la función C las estructuras? ¿Utiliza la matriz de entrada como un buffer, o genera nuevas estructuras con malloc? –

+0

Ah, y también tu declaración de C# parece incorrecta. Un carácter sin signo no es del mismo tamaño que un doble (1 vs 8 bytes) –

+0

@JasonLarke, Desafortunadamente, la documentación de la biblioteca DLL no indica si asigna memoria para la estructura o no. Estoy asumiendo que no es así. No le proporcioné la desaceleración completa para 'Datos', de hecho es un char variable que puede tomar un máximo de 8 bytes y de ahí la razón detrás de ordenarlo como un doble, esta variable definitivamente devuelve los resultados correctos cada vez Lo he probado – user1470994

Respuesta

6

De acuerdo, parece que su biblioteca nativa realiza la asignación, de modo que todo lo que necesita hacer es proporcionar un puntero a través del cual puede acceder a los datos asignados.

Cambiar la definición API para (nota, he cambiado el parámetro maxdata a uint, larga es de 64 bits de .NET y 32 bits en nativo.

[DllImportAttribute("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData); 

De la parte superior de mi cabeza no puedo . me acuerdo bien si necesita la palabra clave a cabo para el parámetro final, pero creo que sí

Luego, llame myData:

uint nAllocs = 0, time = 0; 
IntPtr pAllocs = IntPtr.Zero; 
myData(1, "description", out time, out nAllocs, out pAllocs); 

Ahora, pAllocs deben apuntar a la memoria no administrada, para reunir estos int o memoria administrada no es demasiado difícil:

[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)] 
public struct DATASTRUCT 
{ 
    public byte Rel; 
    public long Time; 
    public byte Validated; 
    public IntPtr Data; //pointer to unmanaged string. 
} 


int szStruct = Marshal.SizeOf(typeof(DATASTRUCT)); 
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs]; 
for(uint i = 0; i < nallocs; i++) 
    localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT)); 

Y ahora debe tener una serie de estructuras locales.

Un punto a tener en cuenta Es posible que tenga que configurar su proyecto para compilar como x86, para estandarizar el tamaño de un IntPtr a 4 bytes (DWORD) en lugar de por defecto de 8 Cualquier CPU.

+0

Gracias Jason, entonces creo que estamos llegando a algún lado. Ahora obtengo la cantidad correcta de estructuras devueltas, así como los datos que llenan las estructuras. Cuando utilizo 'public IntPtr Data;' los datos devueltos para 'data' parecen basura, si lo cambio de nuevo a' public double Data; 'devuelve los datos correctamente. – user1470994

0

Un puntero a un puntero podría ser representado en su declaración dllimport como datos ref IntPtr, por lo que su declaración se convertiría en:

[DllImportAttribute("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data); 

(Dicho sea de paso, creo mucho en C es sólo equivalente a una int en C#. largo en C# es un Int64, lo que sería un largo tiempo en C)

Marshalling su DATASTRUCT [] a un IntPtr se puede hacer uso de la clase GCHandle

DATASTRUCT [] dataInformation = new DATASTRUCT[3]; 
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned); 
IntPtr ptr = gch.AddrOfPinnedObject(); 
myData(myHandle, dataToShow, out Time, out maxData, ref ptr); 
//It's absolutely essential you do this next bit so the object can be garbage collected again, 
//but it should only be done once the unmanaged code is definitely done with the reference.  
gch.Free(); 

Usar la clase Marshal y los métodos StructureToPtr o Copy también sería una opción, pero a los efectos de probar el concepto, al menos, el GCHandle debería hacer el truco, simplemente no es ideal para escenarios donde el código no administrado hace largas operaciones porque usted He inmovilizado este objeto en su lugar y el GC no puede moverlo hasta que lo liberes.

+0

Intenté lo anterior, devuelve las 3 estructuras en la matriz de información de datos, sin embargo, están todas vacías. También edité mi publicación original para hacer referencia al Int64 adecuadamente, ya que de hecho es un __int64. – user1470994

+0

¿Ha actualizado también la definición de C# struct? El carácter sin signo en el original no se traduciría en un doble como lo observó @JasonLarke en los otros comentarios. Si obtiene el tamaño de la estructura ¿coincide con el tamaño esperado según el código C? – Nanhydrin

+0

Puede valer la pena probar Marshal y simplemente pasar un puntero a un puntero a una matriz de matrices de bytes en lugar de una matriz de DATASTRUCTS. Al menos entonces debería ser capaz de ver si hay algún tipo de bytes allí. – Nanhydrin

Cuestiones relacionadas