Экспорт метода dll из C++ в C#. Зачем мне нужно: "extern" C" "
в моей dll есть метод, который я хочу экспортировать.
//труды:
extern "C" __declspec(dllexport)
//не будет работать
__declspec(dllexport)
Экспорт C++:
extern "C" __declspec(dllexport) int Test();
импорт C#:
[DllImport("CircleGPU2_32.DLL", EntryPoint = "Test",
CallingConvention = CallingConvention.StdCall)]
public static extern int Test();
зачем мне extern "C" ?
4 ответов
основная причина заключается в том, чтобы предотвратить c++ name mangler от искажения имени функции.
попробуйте экспортировать его без extern "C"
и проверьте полученную DLL в Dependency Walker, и вы увидите совсем другое имя для экспортируемой функции.
это связано с искажением имени, выполненным C++.
extern" C "vs no extern"C"
в качестве примера вот что показывает CFF Explorer для таблицы экспорта dll. Первый был построен с помощью компилятора borland на c++. Второй был построен с msvc.
Ordinal FunctionRVA Name RVA Name
00000001 0020E140 0032C2B6 createDevice
00000002 0020E244 0032C2C3 createDeviceEx
0000000D 00328DA4 0032C0C1 @irr@core@IdentityMatrix
0000000E 00328DE4 0032C28A @irr@video@IdentityMaterial
0000000C 000F9C80 001EE1B6 createDevice
0000000D 000F9CE0 001EE1C3 createDeviceEx
00000001 00207458 001EDDC7 ?IdentityMaterial@video@irr@@3VSMaterial@12@A
00000002 001F55A0 001EDDF5 ?IdentityMatrix@core@irr@@3V?$CMatrix4@M@12@B
первые 2 функции createDevice
и createDeviceEx
содержат extern "C"
подпись в его прототипе, в то время как другие этого не делают. Обратите внимание на разницу в кодировке при использовании C++ mangling. Этот разница на самом деле идет глубокий чем.
ABI & стандартизация
как объясняется в других ответах, стандарт C++ делает не указать Abstract Binary яминимальная подписка. Это означает, что поставщики, которые разрабатывают свои инструменты, могут в значительной степени делать все, что они хотят, когда речь идет о том, как обрабатываются вызовы функций и как перегрузка работает под капотом-до тех пор, пока она демонстрирует ожидаемое поведение, продиктованное стандартом.
со всеми этими различными схемами кодирования другой язык не может иметь никакой надежды работать с модулями, скомпилированными с C++. Модули Heck, скомпилированные с одним компилятором c++, вряд ли будут работать с другим! Поставщики компиляторов могут свободно изменять свою кодировку между версиями в своих сдержанность.
кроме того, отсутствие общего ABI означает, что нет общего ожидаемого способа вызова этих функций/методов. Например, один компилятор может передавать свои аргументы в стеке, а другой-в регистре. Один может передавать аргументы слева направо, а другой-наоборот. Если только один из этих аспектов не соответствует точно между вызывающим абонентом и вызываемым, то ваше приложение будет сбой... если тебе повезет. Вместо того, чтобы заниматься этим, поставщики просто скажите "нет", заставив ошибку сборки с различными кодировками.
OTOH, в то время как C также не имеет стандартизированного ABI, C-гораздо более простой язык для сравнения. Большинство поставщиков компилятора C обрабатывают украшение функций и механизм вызова аналогичным образом. В результате существует своего рода "де-факто" стандарт, даже если ABI явно не указан в стандарте. Благодаря этой общности другим языкам намного проще взаимодействовать с компилируемыми модулями с С.
например, a __stdcall
украшение в сигнатуре функции понимается как следование определенному соглашению о вызове. Аргументы нажимаются справа налево, и вызываемый отвечает за очистку стека после этого. __cdecl
похоже, но понятно, что абонента отвечает за очистку стека.
На Балансе
если рассматриваемый модуль должен быть совместим с языками за пределами C++, украшая его соответственно и разоблачение его как API C - это ваш лучший выбор. Обратите внимание, что вы отказываетесь от некоторой гибкости, делая это. В частности, вы не сможете перегрузить эти функции, так как компилятор больше не может генерировать уникальные символы для каждой перегрузки с искажением имени.
если совместимость не важна для рассматриваемого модуля - он будет использоваться только с тем же инструментом, с которым он был построен, а затем опустите extern "C"
украшения из прототипов.
компилятор обычно украшает ваши экспортированные имена, чтобы включить информацию о классе и подписи. extern "C"
сообщает компилятору не делать этого.
Я пробовал эту функцию с 1 параметр и вы должны использовать
[DllImportAttribute ("что угодно.Dll", CallingConvention = CallingConvention.ключевое слово cdecl)]
для импорта в C# для работы. Оператор Stdcall работает только (в представленной ситуации, а не в целом ) для функций без параметров и возвращающих void. Протестировано в VS2012 express edition.
в качестве примечания, зависимость walker можно загрузить изhttp://www.dependencywalker.com/