В настоящее время я разрабатываю библиотеку C ++ для Windows, которая будет распространяться как DLL. Моя цель - максимизировать бинарную совместимость; точнее, функции в моей DLL должны использоваться из кода, скомпилированного с несколькими версиями MSVC ++ и MinGW, без необходимости перекомпилировать DLL. Однако я не понимаю, какое соглашение о вызовах лучше всего cdecl
или stdcall
.
Иногда я слышу такие утверждения, как «Соглашение о вызовах C - единственное, которое гарантированно будет одинаковым для всех компиляторов», что контрастирует с такими утверждениями, как « Существуют некоторые вариации в интерпретации cdecl
, особенно в том, как возвращать значения ». Похоже, что это не мешает некоторым разработчикам библиотек (например, libsndfile ) использовать соглашение о вызовах C в распространяемых ими библиотеках DLL без каких-либо видимых проблем.
С другой стороны, stdcall
соглашение о вызовах кажется четко определенным. Из того, что мне сказали, все компиляторы Windows в основном должны следовать ему, потому что это соглашение, используемое для Win32 и COM. Это основано на предположении, что компилятор Windows без поддержки Win32 / COM будет не очень полезен. Многие фрагменты кода, размещенные на форумах, объявляют функции как, stdcall
но я не могу найти ни одного сообщения, которое четко объясняет, почему .
Там слишком много противоречивой информации, и каждый поиск, который я выполняю, дает мне разные ответы, что на самом деле не помогает мне выбрать между ними. Я ищу четкое, подробное, аргументированное объяснение того, почему я должен выбрать одно из них (или почему они эквивалентны).
Обратите внимание, что этот вопрос относится не только к «классическим» функциям, но и к вызовам виртуальных функций-членов, поскольку большая часть клиентского кода будет взаимодействовать с моей DLL через «интерфейсы», чистые виртуальные классы (следующие шаблоны, описанные, например, здесь и там ).
источник
Ответы:
Я только что провел небольшое тестирование в реальном мире (компилирование DLL и приложений с помощью MSVC ++ и MinGW, а затем их смешивание). Как оказалось, у меня были лучшие результаты с
cdecl
соглашением о вызовах.Более конкретно: проблема
stdcall
заключается в том, что MSVC ++ искажает имена в таблице экспорта DLL, даже при использованииextern "C"
. Напримерfoo
делается_foo@4
. Это происходит только при использовании__declspec(dllexport)
, а не при использовании файла DEF; однако, на мой взгляд, файлы DEF представляют собой проблему при обслуживании, и я не хочу их использовать.Изменение имени MSVC ++ создает две проблемы:
GetProcAddress
библиотеки DLL становится немного сложнее;foo@4
вместо_foo@4
), что усложняет связывание. Кроме того, это создает риск того, что «версии без подчеркивания» DLL и приложений будут появляться в «дикой природе», которые несовместимы с «версиями с подчеркиванием».Я пробовал это
cdecl
соглашение: совместимость между MSVC ++ и MinGW работает отлично, сразу после установки, а имена остаются незакрашенными в таблице экспорта DLL. Это работает даже для виртуальных методов.По этим причинам
cdecl
для меня это явный победитель.источник
extern "C"
)?Самая большая разница в двух соглашениях о вызовах заключается в том, что " _ _cdecl" возлагает бремя балансировки стека после вызова функции на вызывающего, что позволяет использовать функции с переменным количеством аргументов. Соглашение «__stdcall» «проще» по своей природе, но менее гибко в этом отношении.
Кроме того, я считаю, что управляемые языки по умолчанию используют соглашение stdcall, поэтому любой, кто использует P / Invoke, должен будет явно указать соглашение о вызовах, если вы используете cdecl.
Итак, если все ваши сигнатуры функций будут статически определены, я бы, вероятно, склонился к stdcall, если не к cdecl.
источник
С точки зрения безопасности это
__cdecl
соглашение «безопаснее», потому что именно вызывающему объекту необходимо освободить стек. В__stdcall
библиотеке может произойти то, что разработчик мог забыть правильно освободить стек, или злоумышленник может внедрить некоторый код, повредив стек DLL (например, подключением API), который затем не проверяется вызывающей стороной. У меня нет примеров безопасности CVS, показывающих, что моя интуиция верна.источник