2016-06-09 40 views
11

Ho bisogno di passare un argomento a una funzione DllImported pericoloso sotto forma di:Come ottenere char ** usando C#?

[DllImport("third_party.dll")] 
private static extern unsafe int start(int argc, char** argv); 

sto supponendo che è un array di stringhe. Tuttavia quando provo a fare quanto segue, ottengo l'errore "Can not conversion from string [] to char **". Come faccio a far funzionare questo? Grazie.

string[] argv = new string[] { }; 
start(0, argv); 

EDIT 1: La questione è stata contrassegnata come duplicato, ma guardando la possibile domanda duplicato, io ancora non vedo come ottenere questo al lavoro.

MODIFICA 2: Per definire ulteriormente la domanda e i parametri richiesti. Assomiglia ai tuoi parametri standard argc/argv (conteggio dei parametri e quindi valori dei parametri). Allo stesso modo in cui avvieresti un programma c: int main(int argc, char** argv); Per questo particolare problema, non voglio passare alcun argomento (quindi il conteggio è 0).

MODIFICA 3: Ho ricevuto ulteriori informazioni dal fornitore di librerie di terze parti. Qui è:

  • il primo parametro è il conteggio degli argomenti
  • il secondo parametro è un array di null stringhe terminate
  • le corde sono ANSI codificato

EDIT 4: modifica finale con una soluzione funzionante (almeno nel mio caso). Vorrei rendere questa la risposta, ma non posso perché questa domanda è contrassegnata come duplicata. Ecco un link per una domanda che mi ha aiutato di più. Alla fine la funzione dll prevedeva un array di puntatori ai buffer con stringhe ANSI. Quindi il mio approccio finale (basato sulla domanda collegata) era il seguente. Creare una matrice in memoria per contenere i puntatori, quindi allocare ogni stringa altrove nella memoria e scrivere i puntatori a quelle stringhe all'interno della prima matrice di puntatori. Questo codice funziona nella produzione:

[DllImport("third_party.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
private static extern int start(Int32 args, IntPtr argv); 

public bool start(params string[] arguments) 
{ 
    int result; 

    if (arguments == null || arguments.Length == 0) 
    { 
     result = dll_system_startup(0, IntPtr.Zero); 
    } 
    else 
    { 
     List<IntPtr> allocatedMemory = new List<IntPtr>(); 

     int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr)); 
     IntPtr pointersToArguments = Marshal.AllocHGlobal(sizeOfIntPtr * arguments.Length); 

     for (int i = 0; i < arguments.Length; ++i) 
     { 
      IntPtr pointerToArgument = Marshal.StringToHGlobalAnsi(arguments[i]); 
      allocatedMemory.Add(pointerToArgument); 
      Marshal.WriteIntPtr(pointersToArguments, i * sizeOfIntPtr, pointerToArgument); 
     } 

     result = start(arguments.Length, pointersToArguments); 

     Marshal.FreeHGlobal(pointersToArguments); 

     foreach (IntPtr pointer in allocatedMemory) 
     { 
      Marshal.FreeHGlobal(pointer); 
     } 
    } 

    return result == 0; 
} 
+0

'char *' So come fare, ma 'char **' ...? Argh. – code4life

+2

Concordato: la domanda è concisa.Non l'ho fatto quindi non posso rispondere :) –

+0

Penso che sia necessario utilizzare StringBuilder [] invece di string [] – RomCoo

risposta

0

Si può cambiare il tipo di parametro di dichiarazione DllImport a StringBuilder[]? Quindi è possibile chiamare la funzione come:

StringBuilder[] args = new StringBuilder[] { }; 
start(args); 
3

Penso che potrebbe essere necessario utilizzare Maresciallo.

var a = (char*)Marshal.StringToCoTaskMemAuto("myString"); 
char** = &a; 

Questa è solo una supposizione, perché non ho una libreria che prende char ** quindi non sono stato in grado di provarlo.

+1

È stato compilato, senza che io debba modificare la firma DllImport, ma mi trovo ancora nell'eccezione PInvokeStackImbalance. La chiamata sembra funzionare parzialmente, perché vedo la registrazione di dll di terze parti nella finestra di output. Devo verificare che qualcos'altro non lo stia causando, prima di accettare la tua risposta. +1 per ora. – Eternal21

+0

@ Eternal21 Dato l'errore PInvokeStackImbalance, ho intenzione di indovinare che avevi ragione sul fatto che fosse "una serie di stringhe", anche se sarebbe davvero utile vedere più dettagli sulla libreria di terze parti. Steve è sulla buona strada che dovresti usare Marshal per convertire le stringhe in puntatori non sicuri, ma sto pensando che dovrai capire che cosa dovrebbero essere quelle stringhe e quindi costruire il tuo array dalle tue stringhe. –

2

L'equivalente di C char** è un byte[][] completamente inserito in C# (e con il pin completo si intende l'array esterno E tutti gli array interni). Se si desidera passare le stringhe C#, è necessario convertirle in array di byte, ad esempio con Encoding.ASCII.GetBytes.

+0

Ho deciso di "marcare" manualmente i dati come buffer a byte singolo []. Ho modificato la mia domanda con il nuovo codice che ho creato. Sembra che dovrebbe funzionare, ma la libreria non mi piace, finisco con Violazione di accesso da esso. Non sono sicuro se è perché ho usato IntPtr invece di byte **. – Eternal21

+0

La firma di dllimport probabilmente non è corretta. È improbabile che la funzione si aspetti un C# 'char **', che sarebbe come un 'wchar_t **' in C. Probabilmente dovrebbe essere semplicemente 'stringa []' e lasciare che P/Invoke gestisca il marshalling, come nel domande duplicate collegate. – Asik

+0

La funzione non prevede un C# char **, motivo per cui ho usato un byte come suggerito. – Eternal21