2012-01-19 13 views
5

Ho la seguente intestazione funzione in una DLL nativa:maresciallo un unsigned char * Funzione di ritorno da una dll, in C#

unsigned char* Version_String() 

Sto cercando di chiamare da un progetto C#, ho provato la seguente chiamata (come si trova su altre domande simili qui):

[DllImport("BSL430.dll", CharSet=CharSet.Ansi)] 
public extern static UIntPtr Version_String(); 

e continuo a ricevere la seguente eccezione:

ha tentato di leggere o scrivere prot memoria Questo è spesso un'indicazione che un'altra memoria è corrotta.

Il tentativo successivo è stato il seguente e ho la stessa eccezione:

[DllImport("BSL430.dll", CharSet=CharSet.Ansi)] 
[return : MarshalAs(UnmanagedType.LPStr)] 
public extern static string Version_String(); 

non riesco per aggirare questo problema. Qualsiasi aiuto sarebbe molto apprezzato!

Edit:

non posso dare il codice DLL qui, in quanto cade sotto un NDA, ma la funzione sto chiamando simile a questa:

unsigned char versionString[50]; 

__declspec(dllexport) unsigned char* Version_String() 
{ 
    if(check_hardware_stuff()) 
    { 
     strcpy((char *) versionString, "version_string_bla_bla"); 
     versionString[5] = stuff; 
    } 
    else if (other_check()) 
    { 
     //will return empty string, that should be filled with '\0' 
    } 
    else 
    { 
     strcpy((char *) versionString, "ERROR"); 
    } 
    return versionString; 
} 

Sono non mi piace particolarmente l'implementazione della DLL, ma ho bisogno di usarla "così com'è". Ricevo l'eccezione generata ogni volta che provo a chiamare VersionString(), indipendentemente da ciò che faccio del valore restituito.

+0

Forse stai usando sbagliato [convenzione chiamata] (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention.aspx)? – svick

+0

La tua DLL è stata compilata con l'opzione charset MultiByte? –

+1

Utilizzare il debugger per scoprire dove si arresta il codice nativo. Progetto + Proprietà, Debug, selezionare "Abilita debug del codice non gestito". Impostare un punto di interruzione sulla funzione nel file del codice sorgente C. –

risposta

8

Aggiornamento

aver visto la domanda aggiornato, i vari commenti, e il codice della funzione nativa, sembra probabile che l'eccezione viene sollevata quando si chiama check_hardware_stuff(). È abbastanza semplice eseguire il debug. Sostituirei la tua funzione con una di questo tipo:

unsigned char versionString [50];

__declspec(dllexport) unsigned char* Version_String() 
{ 
    strcpy(versionString, "testing"); 
    return versionString; 
} 

Se non funziona ancora, allora la mia ipotesi è che l'errore viene generato nel DllMain della DLL. Effettuate il debug mettendo la suddetta funzione in una semplice DLL di vaniglia che non fa altro.

risposta originale

convenzione di chiamata è il problema più evidente. Molto probabilmente il tuo codice nativo utilizza cdecl ma l'impostazione predefinita di p/invoke è stdcall. Cambia la tua p/richiamare la firma per essere come questo:

[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)] 
public extern static IntPtr Version_String(); 

È possibile omettere in modo sicuro il parametro CharSet poiché nessuno dei parametri hanno testo perché si sta trattando il valore restituito come un puntatore.

Modifica: Hans sottolinea correttamente nei commenti che poiché non ci sono parametri, la convenzione di chiamata errata corrisponde non importa. Quindi questo non è il problema.

Chiamare Marshal.PtrToStringAnsi per convertire in una stringa .net.

string version = Marshal.PtrToStringAnsi(Version_String()); 

Dal PtrToStringAnsi si aspetta un parametro IntPtr mi sento di raccomandare di utilizzare IntPtr come il tipo di ritorno di voi P/Invoke firma.

Questo presuppone che la memoria restituita dalla funzione nativa sia allocata e liberata nella DLL nativa. Se è allocata l'heap e ci si aspetta che il chiamante lo disimpegni, si ha un piccolo problema. Come si rilascia la memoria da C# poiché non si ha accesso all'heap della DLL nativa?

La soluzione semplice consiste nell'utilizzare l'heap COM condiviso per allocare la memoria. Chiama CoTaskMemAlloc per allocare il buffer per la stringa. Quindi dichiarare che il valore restituito è di tipo string e che il marshaller p/invoke deallocerà con l'allocatore COM.

[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)] 
public extern static string Version_String(); 
... 
string version = Version_String(); 

Naturalmente, questo vale solo se si restituisce heap di memoria che ci si aspetta il chiamante deallocare allocato.

+0

Grazie per la pronta risposta. Ho provato il codice, ma sto ancora ricevendo le stesse eccezioni. Ho dato un'occhiata al codice DLL e da quello che posso dire la stringa è dichiarata globalmente come una matrice a dimensione fissa: 'unsigned char versionString [50];'. Da quello che so, non dovrebbero essere richieste ulteriori assegnazioni in questo caso, giusto? – andreiscurei

+1

Non può essere CallingConvention, la funzione non accetta argomenti. –

+0

A che punto si ottiene l'eccezione? Quando si chiama la DLL o quando si fa qualcosa con il valore che viene restituito? –