Esportazione funzioni da una DLL con dllexport
-
22-08-2019 - |
Domanda
Vorrei un semplice esempio di esportazione di una funzione da un C ++ DLL di Windows.
Mi piacerebbe vedere l'intestazione, il file cpp, e il file DEF (se assolutamente necessario).
Mi piacerebbe il nome esportato per essere non decorato . Mi piacerebbe utilizzare la convenzione di chiamata più standard (__stdcall?). Mi piacerebbe l'uso __ declspec (dllexport) e non è necessario utilizzare un file DEF.
Ad esempio:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Sto cercando di evitare il linker sottolineatura e / o numeri (conteggi byte?) Aggiunto al nome.
Sto bene con non sostenere dllimport e dllexport utilizzando la stessa intestazione. Non voglio alcuna informazioni sull'esportazione di metodi di classe C ++, basta c-style funzioni globali.
UPDATE
Non compresa la convenzione di chiamata (e utilizzando extern "C"), mi dà i nomi di esportazione come mi piace, ma che cosa significa? È tutto ciò che di default convenzione di chiamata sto ottenendo quello che PInvoke (NET), dichiarare (VB6), e GetProcAddress si aspetta? (Credo che per GetProcAddress dipenderebbe la funzione puntatore del chiamante creato).
Voglio che questa DLL per essere utilizzato senza un file di intestazione, quindi non ho davvero bisogno di un sacco di # definisce fantasia per rendere l'intestazione utilizzabile da un chiamante.
Sono OK con una risposta è che devo utilizzare un file DEF.
Soluzione
Se si desidera esportazioni C normale, utilizzare un progetto di C non C ++. C ++ DLL si affidano a nome-pressare per tutti i ismi C ++ (namespace, ecc ...). È possibile compilare il codice, come C andando in impostazioni del progetto in C / C ++ -> Avanzate, c'è l'opzione "Compila As", che cooresponds alle opzioni del compilatore / TP e / TC
.esportazione / importazione DLL Libs in VC ++
Che cosa si vuole veramente fare è definire una macro condizionale in un colpo di testa che sarà incluso in tutti i file di origine nel progetto DLL:
#ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
#else
# define LIBRARY_API __declspec(dllimport)
#endif
Poi su una funzione che si desidera essere esportati si utilizza LIBRARY_API
:
LIBRARY_API int GetCoolInteger();
Nel progetto di costruzione della libreria creare una definiscono LIBRARY_EXPORTS
questo farà sì che le funzioni da esportare per la build DLL.
Dato che LIBRARY_EXPORTS
non sarà definito in un progetto che consumano la DLL, quando quel progetto include il file di intestazione della libreria tutte le funzioni saranno importati al posto.
Se la libreria è quello di essere cross-platform è possibile definire LIBRARY_API come nulla se non su Windows:
#ifdef _WIN32
# ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
# else
# define LIBRARY_API __declspec(dllimport)
# endif
#elif
# define LIBRARY_API
#endif
Quando si utilizza dllexport / dllimport non è necessario utilizzare i file DEF, se si utilizzano i file DEF non è necessario utilizzare dllexport / dllimport. I due metodi eseguire la stessa operazione modi diversi, credo che dllexport / dllimport è il metodo consigliato fuori dei due.
Esportazione funzioni unmangled da un C ++ DLL per LoadLibrary / PInvoke
Se avete bisogno di questo per utilizzare LoadLibrary e GetProcAddress, o forse facendo PInvoke da NET è possibile utilizzare extern "C"
in linea con il vostro dllexport. E dal momento che stiamo utilizzando GetProcAddress invece di dllimport non abbiamo bisogno di fare la danza ifdef dall'alto, solo un semplice dllexport:
Il codice:
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
EXTERN_DLL_EXPORT int getEngineVersion() {
return 1;
}
EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
K.getGraphicsServer().addGraphicsDriver(
auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
);
}
Ed ecco che cosa esportazioni assomigliano con DUMPBIN / export:
Dump of file opengl_plugin.dll
File Type: DLL
Section contains the following exports for opengl_plugin.dll
00000000 characteristics
49866068 time date stamp Sun Feb 01 19:54:32 2009
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
2 1 00011028 registerPlugin = @ILT+35(_registerPlugin)
Quindi, questo codice funziona bene:
m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");
m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
::GetProcAddress(m_hDLL, "registerPlugin")
);
Altri suggerimenti
Per C ++:
Ho appena affrontato lo stesso problema e penso che vale la pena ricordare un problema si presenta quando si usano entrambi __stdcall
(o WINAPI
) e extern "C"
:
Come sapete extern "C"
rimuove la decorazione in modo che invece di:
__declspec(dllexport) int Test(void) --> dumpbin : ?Test@@YaHXZ
si ottiene un nome di simbolo non decorato:
extern "C" __declspec(dllexport) int Test(void) --> dumpbin : Test
Tuttavia il _stdcall
(= macro WINAPI, che modifica la convenzione di chiamata) decora anche nomi in modo che se usiamo entrambi otteniamo:
extern "C" __declspec(dllexport) int WINAPI Test(void) --> dumpbin : _Test@0
e il beneficio della extern "C"
si perde perché il simbolo è decorata (con _ @bytes)
Si noti che questo solo si verifica per l'architettura x86, perché la convenzione
__stdcall
viene ignorato su x64 ( MSDN : su x64 architetture, per convenzione, gli argomenti vengono passati nei registri quando possibile, e successive argomenti sono passati sullo stack .).
Questo è particolarmente difficile se si prendono di mira entrambe le piattaforme x86 e x64.
due soluzioni
-
Utilizzare un file di definizione. Ma questo ti costringe a mantenere lo stato del file def.
-
il modo più semplice: definire la macro (vedi MSDN ):
Commento #define EXPORT (linker, "/ EXPORT:" __FUNCTION__ "=" __FUNCDNAME __)
e quindi includere il seguente pragma nel corpo della funzione:
#pragma EXPORT
Esempio completa:
int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
In questo modo esportare la funzione non decorato per entrambi i target x86 e x64, preservando la convenzione __stdcall
per x86. Il __declspec(dllexport)
non è necessaria in questo caso.
Ho avuto esattamente lo stesso problema, la mia soluzione era quella di utilizzare file di definizione di modulo (DEF), invece di __declspec(dllexport)
per definire le esportazioni ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). Non ho idea del perché questo funziona, ma lo fa
Credo _naked potrebbe ottenere ciò che si vuole, ma impedisce anche il compilatore di generare il codice di gestione dello stack per la funzione. extern "C" provoca nome decorazione in stile C. Rimuovere questo e che dovrebbe sbarazzarsi del vostro _ di. Il linker non aggiunge le sottolineature, il compilatore fa. stdcall causa la dimensione stack degli argomenti da aggiungere.
Per ulteriori informazioni, consultare: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
La domanda più importante è perché vuoi farlo? Cosa c'è di sbagliato con i nomi alterati?