Экспорт функций из DLL с помощью dllexport
Я хотел бы простой пример экспорта функции из C++ Windows DLL.
Я хотел бы видеть заголовок, файл cpp и файл def (если это абсолютно необходимо).
Я бы хотел, чтобы экспортированное имя было неукрашенным. Я хотел бы использовать самое стандартное соглашение о вызовах (__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++, просто глобальные функции в стиле c.
ОБНОВИТЬ
Не включая соглашение о вызовах (и использование extern "C") дает мне имена экспорта, как мне нравится, но что это значит? Какое соглашение о вызовах по умолчанию я получаю, чего ожидают pinvoke (.NET), Declare (vb6) и GetProcAddress? (Я полагаю, что для GetProcAddress это будет зависеть от указателя функции, созданной вызывающей стороной).
Я хочу, чтобы эта DLL использовалась без заголовочного файла, поэтому мне не нужно много причудливых #defines, чтобы заголовок мог использоваться вызывающей стороной.
Я согласен с ответом, что я должен использовать файл DEF.
4 ответа
Если вы хотите простой экспорт C, используйте проект C, а не C++. DLL-библиотеки C++ основаны на распределении имен для всех измов C++ (пространства имен и т. Д.). Вы можете скомпилировать ваш код как C, зайдя в настройки вашего проекта в C/C++->Advanced, есть опция "Compile As", которая соответствует переключателям компилятора /TP и /TC.
Экспорт / импорт библиотек DLL в VC++
Что вы действительно хотите сделать, это определить условный макрос в заголовке, который будет включен во все исходные файлы в вашем проекте DLL:
#ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
#else
# define LIBRARY_API __declspec(dllimport)
#endif
Затем на функцию, которую вы хотите экспортировать, вы используете LIBRARY_API
:
LIBRARY_API int GetCoolInteger();
В вашем проекте сборки библиотеки создайте определение 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 является рекомендуемым методом из двух.
Экспорт неупорядоченных функций из C++ 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")
);
Для C++:
Я только что столкнулся с той же проблемой, и я думаю, что стоит упомянуть, что проблема возникает, когда один из __stdcall
(или же WINAPI
) и extern "C"
:
Как Вам известно extern "C"
удаляет украшение так, чтобы вместо:
__declspec(dllexport) int Test(void) --> dumpbin : ?Test@@YaHXZ
Вы получаете имя символа без украшений:
extern "C" __declspec(dllexport) int Test(void) --> dumpbin : Test
Тем не менее _stdcall
( = макрос WINAPI, который изменяет соглашение о вызовах) также украшает имена, так что если мы используем оба, мы получим:
extern "C" __declspec(dllexport) int WINAPI Test(void) --> dumpbin : _Test@0
и польза extern "C"
теряется, потому что символ украшен (_ _bytes)
Обратите внимание, что это происходит только для архитектуры x86, потому что
__stdcall
в x64 соглашение игнорируется ( msdn: в архитектурах x64, по соглашению, аргументы передаются в регистры, когда это возможно, и последующие аргументы передаются в стек.).
Это особенно сложно, если вы нацелены на платформы как x86, так и x64.
Два решения
Используйте файл определения. Но это заставляет вас поддерживать состояние файла def.
самый простой способ: определить макрос (см. msdn):
#define Комментарий EXPORT (компоновщик, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
и затем включите следующую прагму в тело функции:
#pragma EXPORT
Полный пример:
int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
Это позволит экспортировать функцию без оформления для целей x86 и x64 при сохранении __stdcall
конвенция для x86. __declspec(dllexport)
не требуется в этом случае.
У меня была точно такая же проблема, мое решение было использовать файл определения модуля (.def) вместо __declspec(dllexport)
определить экспорт ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx). Я понятия не имею, почему это работает, но это делает
Я думаю, что _naked может получить то, что вы хотите, но это также не позволяет компилятору генерировать код управления стеком для функции. extern "C" вызывает оформление имени в стиле C. Удалите это, и это должно избавиться от ваших _. Компоновщик не добавляет подчеркивания, компилятор добавляет. stdcall добавляет размер стека аргументов.
Для получения дополнительной информации см.: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
Большой вопрос: почему вы хотите это сделать? Что не так с искаженными именами?