2012-04-02 18 views
5

Ho un 3rd party libreria C, che uno dei suo metodo esportato è la seguente:iterazione su memoria allocata con Marshal.AllocHGlobal()

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

Il primo argomento è popolato con puntatori in argomento buf in cui specifica le stringhe sono memorizzate. La dimensione indica per quanto tempo ciascuna stringa dovrebbe essere nel buffer buf. Il mio C# P/metodo Invoke per questo attualmente si presenta così:

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

Il C# P/metodo Invoke è privato perché sto avvolgendolo di funzionalità in un metodo pubblico "getter" che gestisce l'assegnazione/deallocazione della memoria per il chiamante. Quando uso questo metodo in C++, l'iterazione è piuttosto semplice. Ho semplicemente fare qualcosa di simile:

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; 
    } 
} 

A causa di come questi vengono utilizzati in C++, ho deciso probabilmente dovrei usare oggetti IntPtr e proprio allocare la memoria che avrò bisogno dal mucchio, mettere in codice nativo, e iterare su di esso come farò in C++. Poi ho ricordato che i caratteri in C# sono caratteri unicode e non caratteri ASCII. L'iterazione in C# funzionerebbe allo stesso modo anche se inserissi l'iterazione in un blocco di codice non sicuro? Il mio primo pensiero è stato quello di effettuare le seguenti operazioni: "Come devo iterare su ciò che questo metodo nativo codice restituisce"

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]; 
    } 
} 

La mia domanda allora è, È questo il modo giusto per effettuare il marshalling a questa funzione? Dovrei usare un oggetto StringBuilder per il secondo argomento? Un problema vincolante è che il primo argomento è una mappatura 1: 1 di una struttura dietro il metodo GetStuff(). Il valore di ciascun indice è cruciale per capire cosa stai guardando nel secondo argomento del buffer.

Apprezzo qualsiasi suggerimento.

Grazie, Andy

risposta

3

io pensi di essere sulla strada giusta, ma mi piacerebbe farlo senza l'utilizzo di codice non sicuro. In questo modo:

[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); 
} 

Questa è una traduzione piuttosto diretta della versione C++.

+0

Mi piace questa proposta e proverò quando qualche hardware si libera (questa libreria di terze parti funziona contro l'hardware condiviso da me e altri due). Sarò sicuro di contrassegnare come risposta se si adatta al conto. Penso che lo farà. Sai, non pensavo di creare un array di IntPtr. È piuttosto lucido. –

+0

Grazie mille per la soluzione. Nel ciclo foreach non ho potuto verificare per item.Zero e quindi il condizionale doveva essere se (item! = IntPtr.Zero). Ho modificato il codice ma deve essere sottoposto a peer review (una precauzione saggia). Grazie ancora. –

+0

@AndrewFalanga Quella soluzione va bene. Significa la stessa cosa. Penso che ci siano stati alcuni cambiamenti in IntPtr con .net 4. Sei in una versione precedente? –

Problemi correlati