Вопрос

Я хотел бы получить простой пример экспорта функции из 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.

Это было полезно?

Решение

Если вам нужен обычный экспорт C, используйте проект C, а не C ++.Библиотеки DLL C ++ полагаются на изменение имен для всех измов C ++ (пространств имен и т.д. ).Вы можете скомпилировать свой код как C, зайдя в настройки вашего проекта в разделе C / C ++-> Дополнительно, там есть опция "Скомпилировать как", которая соответствует переключателям компилятора / 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 как nothing, если не в 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 C ++ для LoadLibrary/PInvoke

Если вам нужно это, чтобы использовать LoadLibrary и GetProcAddress, или, возможно, выполнить PInvoke из .NET вы можете использовать extern "C" встроенный в ваш dllexport.И поскольку мы используем GetProcAddress вместо dllimport, нам не нужно выполнять ifdef dance сверху, достаточно простого 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) :

#определить комментарий К ЭКСПОРТУ(компоновщик, "/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 может получить то, что вы хотите, но это также не позволяет компилятору генерировать код управления стеком для функции.внешняя буква "C" вызывает оформление имени стиля C.Удалите это, и это должно избавить вас от ваших _'s .Компоновщик не добавляет символы подчеркивания, это делает компилятор.stdcall приводит к добавлению размера стека аргументов.

Подробнее см.:http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top