2012-04-02 13 views
5

tengo una tercera parte de la biblioteca C, que uno de su método de exportada es el siguiente:interactuando sobre la memoria asignada con Marshal.AllocHGlobal()

#define MAX_INDEX 8 
int GetStuff(IN char* index[MAX_INDEX], OUT char* buf, IN size_t size); 

El primer argumento se rellena con punteros en el argumento buf donde específica las cadenas se almacenan. El tamaño indica cuánto tiempo se espera que esté cada cadena en el búfer buf. Mi C# P/método Invoke para este momento, se ve así:

[DllImport("Path/To/Dll", CharSet = CharSet.Ansi)] 
private static extern int GetStuff(IntPtr indecies, IntPtr buf, Int32 size); 

El C# P/Invoke método es privada porque yo estoy envolviendo su funcionalidad en un método público "captador", que se encarga de la asignación/desasignación de memoria para la persona que llama. Cuando uso este método en C++, iterar es bastante simple en realidad. Simplemente hacer algo como:

char* pIndecies[MAX_INDEX]; 
char* pBuffer = new char[MAX_INDEX * (256 + 1)]; // +1 for terminating NULL 

GetStuff(pIndecies, pBuffer, 256); 

// iterate over the items 
for(int i(0); i < MAX_INDEX; i++) { 
    if(pIndecies[i]) { 
     std::cout << "String for index: " << i << " " << pIndecies[i] << std::endl; 
    } 
} 

Debido a cómo estos se utilizan en C++, decidí que probablemente debería utilizar objetos IntPtr y simplemente asignar la memoria que necesitaré del montón, poner en el código nativo, e iterar sobre él de la manera que lo haría en C++. Entonces recordé que los caracteres en C# son caracteres unicode y no caracteres ASCII. ¿La iteración en C# funcionaría de la misma manera aunque colocara la iteración en un bloque de código inseguro? Mi primera idea fue hacer lo siguiente: "¿Cómo debo iterar sobre lo que este nativo devuelve el método de código"

IntPtr pIndecies = Marshal.AllocHGlobal(MAX_INDEX * 4); // the size of a 32-pointer 
IntPtr pBuffer = Marshal.AllocHGlobal(MAX_INDEX * (256 + 1)); // should be the same 
NativeMethods.GetStuff(pIndecies, pBuffer, 256); 

unsafe { 
    char* pCStrings = (char*)pIndecies.ToPointer(); 
    for(int i = 0; i < MAX_INDEX; i++) { 
     if(pCStrings[i]) 
      string s = pCStrings[i]; 
    } 
} 

Mi pregunta entonces es, ¿Es esta la forma correcta de llegar a esta función? ¿Debo usar un objeto StringBuilder para el segundo argumento? Un problema limitante es que el primer argumento es un mapeo 1: 1 de una estructura detrás del método GetStuff(). El valor de cada índice es crucial para comprender lo que está viendo en el segundo argumento de la memoria intermedia.

Agradezco cualquier sugerencia.

Gracias, Andy

Respuesta

3

Creo que estás en el camino correcto, pero lo haría sin usar código no seguro. De esta manera:

[DllImport("Path/To/Dll", CharSet = CharSet.Ansi)] 
private static extern int GetStuff(IntPtr[] index, IntPtr buf, Int32 size); 
.... 
IntPtr[] index = new IntPtr[MAX_INDEX]; 
IntPtr pBuffer = Marshal.AllocHGlobal(MAX_INDEX * 256 + 1); 
try 
{ 
    int res = NativeMethods.GetStuff(index, pBuffer, 256); 
    // check res for errors? 
    foreach (IntPtr item in index) 
    { 
     if (item != IntPtr.Zero) 
      string s = Marshal.PtrToStrAnsi(item); 
    } 
} 
finally 
{ 
    Marshal.FreeHGlobal(pBuffer); 
} 

Esta es una traducción bastante directa de su versión de C++.

+0

Me gusta esta propuesta y la probaré cuando algún hardware se libere (esta biblioteca de terceros funciona contra hardware que yo y otros dos compartimos). Me aseguraré de marcar como la respuesta si se ajusta a la ley. Creo que lo hará. Ya sabes, no pensé en hacer una variedad de IntPtr's. Eso es bastante astuto. –

+0

Muchas gracias por la solución. En el ciclo foreach no pude verificar el elemento. Zero y, por lo tanto, el condicional tenía que ser if (item! = IntPtr.Zero). He editado el código, pero debe ser revisado por pares (una precaución sabia). Gracias de nuevo. –

+0

@AndrewFalanga Esa solución está bien. Significa lo mismo. Creo que hubo algunos cambios en IntPtr con .net 4. ¿Estás en una versión inferior? –

Cuestiones relacionadas