Экспорт метода 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/