Экспорт функций из DLL с помощью dllexport
Мне нужен простой пример экспорта функции из DLL С++ windows.
Я бы хотел увидеть заголовок, файл cpp и файл def (если это абсолютно необходимо).
Я бы хотел, чтобы экспортированное имя было undecorated. Я хотел бы использовать наиболее стандартное соглашение о вызове (__stdcall?). Мне бы хотелось использовать __ declspec (dllexport) и не использовать DEF файл.
Например:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Я пытаюсь избежать компоновщика добавленных подчеркиваний и/или чисел (количество байтов?) на имя.
Я в порядке, не поддерживая dllimport и dllexport, используя тот же заголовок. Мне не нужна информация об экспорте методов класса С++, а также глобальные функции c-style.
UPDATE
Не включая соглашение о вызове (и использование extern "C" ) дает мне имена экспорта, как мне нравится, но что это значит? Является ли какое-либо соглашение по умолчанию по умолчанию я получаю то, что pinvoke (.NET), declare (vb6) и GetProcAddress будет ожидать? (Я думаю, что для GetProcAddress это будет зависеть от указателя функции, созданного вызывающим).
Я хочу, чтобы эта DLL использовалась без файла заголовка, поэтому мне действительно не нужно много фантастических #defines, чтобы заголовок мог использоваться вызывающим.
Я в порядке с ответом, что мне нужно использовать файл DEF.
Ответы
Ответ 1
Если вам нужен простой экспорт C, используйте проект C, а не С++. С++ DLL полагаются на обработку имен для всех CMS-имен (пространства имен и т.д.). Вы можете скомпилировать свой код как C, перейдя в ваши настройки проекта в разделе C/С++ → Advanced, есть опция "Compile As", которая координирует ключи компилятора /TP и/TC.
Экспорт/Импорт DLL-библиотек в VС++
Что вы действительно хотите сделать, так это определить условный макрос в заголовке, который будет включен во все исходные файлы в вашем проекте DLL:
#ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
#else
# define LIBRARY_API __declspec(dllimport)
#endif
Затем для функции, которую вы хотите экспортировать, вы используете LIBRARY_API
:
LIBRARY_API int GetCoolInteger();
В проекте сборки библиотеки создайте define LIBRARY_EXPORTS
, это приведет к тому, что ваши функции будут экспортированы для вашей сборки DLL.
Так как LIBRARY_EXPORTS
не будет определяться в проекте, использующем DLL, когда этот проект включает в себя заголовочный файл вашей библиотеки, все функции будут импортированы вместо этого.
Если ваша библиотека должна быть кросс-платформенной, вы можете определить LIBRARY_API как ничего, если не в Windows:
#ifdef _WIN32
# ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
# else
# define LIBRARY_API __declspec(dllimport)
# endif
#elif
# define LIBRARY_API
#endif
При использовании dllexport/dllimport вам не нужно использовать файлы DEF, если вы используете файлы DEF, вам не нужно использовать dllexport/dllimport. Два метода выполняют одну и ту же задачу по-разному, я считаю, что dllexport/dllimport является рекомендуемым методом из двух.
Экспорт неперепутанных функций из С++ DLL для LoadLibrary/PInvoke
Если вам нужно использовать LoadLibrary и GetProcAddress, или, возможно, сделать PInvoke из .NET, вы можете использовать встроенную строку extern "C"
с вашим dllexport. И поскольку мы используем GetProcAddress вместо dllimport, нам не нужно делать танк ifdef сверху, просто простой dllexport:
Код:
#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())
);
}
И вот как выглядит экспорт с Dumpbin/exports:
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)
Итак, этот код работает нормально:
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")
);
Ответ 2
Для С++:
Я столкнулся с одной и той же проблемой, и я думаю, что стоит упомянуть, что проблема возникает, когда вы используете как __stdcall
(или WINAPI
) , так и extern "C"
:
Как вы знаете, extern "C"
удаляет декор, чтобы вместо:
__declspec(dllexport) int Test(void) --> dumpbin : [email protected]@YaHXZ
вы получите имя символа unecorated:
extern "C" __declspec(dllexport) int Test(void) --> dumpbin : Test
Однако _stdcall
(= макрос WINAPI, который изменяет соглашение о вызове) также украшает имена, поэтому, если мы используем оба, мы получаем:
extern "C" __declspec(dllexport) int WINAPI Test(void) --> dumpbin : [email protected]
и преимущество extern "C"
теряется, потому что символ украшен (с помощью _ @bytes)
Обратите внимание, что это происходит только для архитектуры x86, потому что соглашение __stdcall
игнорируется на x64 (msdn: по архитектуре x64 по соглашению аргументы передаются в регистры, когда это возможно, и последующие аргументы передаются в стек.).
Это особенно сложно, если вы ориентируетесь на платформы x86 и x64.
Два решения
#define Комментарий EXPORT (компоновщик, "/ЭКСПОРТ:" __FUNCTION__ "=" __FUNCDNAME __)
а затем включить в тело функции следующую прагму:
#pragma EXPORT
Полный пример:
int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
Это будет экспортировать функцию unecorated для целей x86 и x64, сохраняя при этом соглашение __stdcall
для x86. В этом случае требуется __declspec(dllexport)
не.
Ответ 3
У меня была точно такая же проблема, моим решением было использовать файл определения файла (.def) вместо __declspec(dllexport)
для определения экспорта (http://msdn.microsoft.com/en-us/library/d91k01sh.aspx). Я не знаю, почему это работает, но он делает
Ответ 4
Я думаю, что _naked может получить то, что вы хотите, но это также мешает компилятору генерировать код управления стеком для этой функции. extern "C" вызывает декодирование имени стиля C. Удалите это, и это должно избавиться от ваших _. Компилятор не добавляет символы подчеркивания. stdcall вызывает добавление размера стека аргументов.
Подробнее см.
http://en.wikipedia.org/wiki/X86_calling_conventions
http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
Больший вопрос - почему вы хотите это сделать? Что случилось с искаженными именами?