Экспорт функций из 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.


Два решения

  1. Используйте файл определения. Но это заставляет вас поддерживать состояние файла def.

  2. самый простой способ: определить макрос (см. 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

Большой вопрос: почему вы хотите это сделать? Что не так с искаженными именами?

Другие вопросы по тегам