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
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. –
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. –
@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? –