C ++ - это язык, который поддерживает перегрузку.Другими словами, вы можете иметь более одной версии HelloWorld ().Вы также можете экспортировать HelloWorld (int), отдельную версию.Это также язык, который требует компоновщика.Чтобы не путать компоновщик с одинаковыми именами для разных функций, компилятор декорирует имя функции.Aka "name mangling".
Инструмент, который вы хотите использовать для устранения подобных проблем, это Dumpbin.exe.Запустите его из командной строки Visual Studio в вашей DLL с параметром / exports.Вы увидите это:
ordinal hint RVA name
1 0 000110EB ?HelloWorld@@YAXXZ = @ILT+230(?HelloWorld@@YAXXZ)
Куча gobbledegook, экспортированное имя показано в скобках.Обратите внимание ?спереди и @@ YAXXZ после имени, поэтому CLR не может найти экспортированную функцию.Функция, которая принимает аргумент int, будет экспортирована как? HelloWorld @@ YAXH @ Z (попробуйте).
Директива [DllImport] поддерживает это, вы можете использовать свойство EntryPoint, чтобы присвоить экспортируемое имя.Или вы можете сказать компилятору C ++, что он должен генерировать код, который может использовать компилятор C.Поставьте extern "C"
перед объявлением, и компилятор C ++ подавит оформление имени.И, конечно, больше не будет поддерживать перегрузки функций.Dumpbin.exe теперь показывает это:
ordinal hint RVA name
1 0 00011005 HelloWorld = @ILT+0(_HelloWorld)
Обратите внимание, что имя все еще не просто "HelloWorld", перед именем есть подчеркивание.Это украшение, которое помогает поймать ошибки в соглашении о вызовах.В 32-битном коде есть 5 различных способов вызова функции.Три из них являются общими с DLL, __cdecl, __stdcall и __thiscall.Компилятор C ++ по умолчанию равен __cdecl для обычных свободных функций.
Это также свойство атрибута [DllImport], свойства CallingConvention.По умолчанию используется, если он не указан, CallingConvention.StdCall.Что соответствует соглашению о вызовах для многих библиотек DLL, особенно Windows, но не соответствует стандартному компилятору C ++, поэтому у вас все еще есть проблема.Просто используйте свойство или объявите свою функцию C ++ следующим образом:
extern "C" __declspec(dllexport)
void __stdcall HelloWorld() {
// etc..
}
И вывод Dumpbin.exe теперь выглядит так:
ordinal hint RVA name
1 0 000110B9 _HelloWorld@0 = @ILT+180(_HelloWorld@0)
Обратите внимание на добавленный @ 0, он описывает размеркадра активации стека.Другими словами, сколько байтов стоит аргументов.Это помогает отследить ошибку объявления во время соединения, такие ошибки чрезвычайно трудно диагностировать во время выполнения.
Теперь вы можете использовать атрибут [DllImport], как он был у вас изначально, маршаллер pinvoke достаточно умен, чтобы разобратьсяукрашение фактической функции.Вы можете помочь ему со свойствами ExactSpelling и EntryPoint, это будет немного быстрее, но вы ничего не заметите.
Первый вопрос последний: __declspec (dllexport) - это просто подсказка компилятору, который вы собираетесь экспортироватьфункция из DLL.Он сгенерирует лишний код, который поможет ускорить вызов экспортированной функции (ничего, что использует CLR).И передает инструкции компоновщику, что функция должна быть экспортирована.Экспорт функций также можно выполнить с помощью файла .def, но это сложно сделать.