2011-12-16 15 views
8

Nella mia DLL c'è un metodo che voglio esportare.Esporta il metodo dll da C++ a C#. Perché ho bisogno: "extern" C ""

// Opere:

extern "C" __declspec(dllexport) 

// non funzionerà

__declspec(dllexport) 

C++ Export:

extern "C" __declspec(dllexport) int Test(); 

C# importazione:

[DllImport("CircleGPU2_32.DLL", EntryPoint = "Test", 
    CallingConvention = CallingConvention.StdCall)] 
public static extern int Test(); 

Perché ho bisogno del extern "C"?

+0

Ho aggiunto una spiegazione più dettagliata del perché ce n'è bisogno, se siete interessati. – greatwolf

risposta

11

Il motivo principale è impedire al mangler del nome C++ di manomettere il nome della funzione.

Provare ad esportarlo senza il extern "C" e ispezionare la DLL risultante in Dipendente Walker e vedrete un nome abbastanza diverso per la funzione esportata.

+0

Il nome può essere qualcosa o c'è qualche motivo? Che comportamento strano .. Grazie – Pedro77

+0

Sì, c'è uno schema. Il mangling codifica i tipi dei parametri. Ma è specifico per l'implementazione e cambia dal compilatore al compilatore. Quindi, se vuoi la compatibilità binaria, devi evitarlo. –

+0

Mangling è stato introdotto quando il sovraccarico di funzione è stato aggiunto alla lingua. Bjarne voleva utilizzare gli strumenti di linker esistenti che non supportano il sovraccarico (cioè più funzioni differenti con lo stesso nome), quindi è stato inventato il manomissione. –

20

Ciò è dovuto al nome mangling eseguito da C++.

extern "C" vs no extern "C"

Come esempio qui è che cosa CFF Explorer mostra per la tabella di esportazione di una dll. Il primo è stato realizzato con il compilatore C++ di Borland. Il secondo è stato realizzato con msvc.

Ordinal  FunctionRVA  Name RVA Name 
00000001 0020E140  0032C2B6 createDevice 
00000002 0020E244  0032C2C3 createDeviceEx 
0000000D 00328DA4  0032C0C1 @[email protected]@IdentityMatrix 
0000000E 00328DE4  0032C28A @[email protected]@IdentityMaterial 

0000000C 000F9C80  001EE1B6 createDevice 
0000000D 000F9CE0  001EE1C3 createDeviceEx 
00000001 00207458  001EDDC7 [email protected]@[email protected]@[email protected]@A 
00000002 001F55A0  001EDDF5 [email protected]@[email protected]@[email protected]@[email protected] 

I primi 2 funzioni createDevice e createDeviceEx contengono la firma extern "C" nel suo prototipo, mentre gli altri non lo fanno. Si noti la differenza nella codifica quando viene utilizzato il manegging C++. La differenza in realtà va a più profonda di.

ABI & normalizzazione

Come spiegato nelle altre risposte, lo standard C++ fa non specificare un UnbstractBfabbrica di alcune componentihonterface. Ciò significa che i venditori che progettano i loro strumenti possono fare praticamente tutto ciò che vogliono quando si tratta di come vengono gestite le chiamate di funzione e di come funziona il sovraccarico, purché mostri il comportamento previsto dettato dallo standard.

Con tutti questi schemi di codifica diversi, non c'è modo che un altro linguaggio possa sperare di lavorare con i moduli compilati con C++. I moduli Heck compilati con un compilatore C++ è improbabile che funzionino con un altro! I produttori di compilatori sono liberi di cambiare la codifica tra le versioni a loro discrezione.

Inoltre, nessun ABI comune significa che non esiste un modo comune di richiamare queste funzioni/metodi. Ad esempio, un compilatore potrebbe passare i suoi argomenti sullo stack mentre un altro compilatore potrebbe passarlo sul registro. Uno potrebbe passare argomenti da sinistra a destra mentre un altro potrebbe essere invertito. Se solo uno di questi aspetti non corrisponde esattamente tra il chiamante e il chiamato, la tua app si bloccherà ... cioè se sei fortunato. Invece di occuparsi di ciò, i venditori semplicemente dicono di no forzando un errore di compilazione con le diverse codifiche.

OTOH, anche se C non ha un ABI standardizzato, C è un linguaggio molto più semplice da trattare al confronto. La maggior parte dei produttori di compilatori C gestisce la decorazione delle funzioni e il meccanismo di chiamata in modo simile. Di conseguenza c'è una sorta di standard "de facto" anche se un ABI non è esplicitamente specificato nello standard. Con questa comunanza, è molto più semplice per le altre lingue interfacciare con moduli compilati con C.

Ad esempio, una decorazione della funzione __stdcall segue una convenzione di chiamata specifica. Gli argomenti vengono spostati da destra a sinistra e il callee è responsabile della pulizia dello stack in seguito. __cdecl è simile ma è noto che il caller è responsabile della pulizia dello stack.

La linea di fondo

Se il modulo in questione deve essere interoperabile con i linguaggi di fuori di C++, decorando in modo appropriato ed esponendo come un'API C è la soluzione migliore. Nota che stai abbandonando un po 'di flessibilità facendo questo. In particolare, non sarete in grado di sovraccaricare quelle funzioni poiché il compilatore non può più generare simboli univoci per ogni sovraccarico con manomissione del nome.

Se l'interoperabilità non è importante per il modulo in questione, verrà utilizzata solo con lo stesso strumento con cui è stata creata, quindi omettere la decorazione extern "C" dai prototipi.

+0

C non ha un ABI standardizzato. http://stackoverflow.com/questions/4489012/does-c-have-a-standard-abi –

+0

@Charles doh! Credo che tu abbia ragione. Suppongo che lo standard di fatto sia più appropriato qui. – greatwolf

1

Il compilatore normalmente decora i valori i nomi esportati per includere informazioni su classe e firma. extern "C" dice al compilatore di non farlo.

+1

La decorazione è un termine più ampio. L'uso di "extern" C "" interrompe una forma di decorazione, mordenzatura, ma altre decorazioni possono ancora esistere. Ad esempio, i metodi 'stdcall' sono generalmente decorati con un prefisso' _' e un suffisso '@ N', dove N è la dimensione dello stack. http://msdn.microsoft.com/en-us/library/zxk0tw93(v=VS.100).aspx –

0

Ho provato questo con una funzione con 1 parametro e si deve utilizzare

[DllImportAttribute ("whatever.Dll", CallingConvention = CallingConvention. CDECL)]

per l'importazione in C# per funzionare. L'istruzione Stdcall funziona solo (nella situazione presentata, non in generale) per le funzioni senza parametri e che non restituiscono nulla. Testato in edizione express vs2012.

Come nota a margine, il Dependency Walker può essere scaricata dal http://www.dependencywalker.com/