Я пишу C / C++ DLL и хочу экспортировать определенные функции, которые я делал перед использованием файла .def, подобного этому
LIBRARY "MyLib"
EXPORTS
Foo
Bar
с кодом, определенным как это, например:
int Foo(int a);
void Bar(int foo);
Однако что, если я хочу объявить перегруженный метод Foo (), например:
int Foo(int a, int b);
Поскольку файл def имеет только имя функции, а не полный прототип, я не вижу, как он будет обрабатывать перегруженные функции. Вы просто используете одну запись, а затем указываете, какую перегруженную версию вы хотите, передавая правильно прототипированный указатель функции в LoadLibrary ()?
Обновлено: чтобы было ясно, это в Windows с использованием Visual Studio 2005
Обновлено: пометил метод non-def (__declspec) как ответ ... Я знаю, что это на самом деле не решает проблему с использованием файлов def, как я хотел, но похоже, что, вероятно, нет (официального) решения с использованием файлов def. Однако оставим вопрос открытым, на случай, если кто-то что-то знает, у нас нет перегруженных функций и файлов def.





В самом коде отметьте функции, которые вы хотите экспортировать, используя __declspec (dllexport). Например:
#define DllExport __declspec(dllexport)
int DllExport Foo( int a ) {
// implementation
}
int DllExport Foo( int a, int b ) {
// implementation
}
Если вы это сделаете, вам не нужно перечислять функции в файле .def.
В качестве альтернативы вы можете использовать значение параметра по умолчанию, например:
int Foo( int a, int b = -1 )
Это предполагает, что существует значение для b, которое можно использовать, чтобы указать, что оно не используется. Если -1 является допустимым значением для b или если его нет или не должно быть по умолчанию, это не сработает.
Изменить (Адам Хейл): исправлено использование __declspec, поскольку __dllspec было неверным, поэтому я мог отметить это как официальный ответ ... это было достаточно близко.
Изменить (Грэм): Ой - спасибо за исправление моей опечатки!
Затем вам нужно использовать искаженные имена или переименовать одну из функций и сделать их обе extern "C", предполагая, что ни одна из них не принимает или не возвращает объекты C++.
Перегрузка функций - это функция C++, которая полагается на искажение имен (загадочные имена функций в сообщениях об ошибках компоновщика).
Записав искаженные имена в файл def, я могу связать свой тестовый проект и запустить его:
LIBRARY "TestDLL"
EXPORTS
?Foo@@YAXH@Z
?Foo@@YAXHH@Z
кажется, работает для
void Foo( int x );
void Foo( int x, int y );
Поэтому скопируйте имена функций C++ из сообщения об ошибке и запишите их в свой файл def. Однако реальный вопрос: почему вы хотите использовать файл def, а не использовать __declspec (dllexport)?
Искаженные имена непереносимы, я тестировал с VC++ 2008.
Интересный подход. Под непереносимым вы имеете в виду разные версии Visual Studio? Что может подсказать, что они, возможно, меняют свои схемы искажения названия между версиями?
@jxramos Я не уверен, действительно ли они могут изменить схему искажения имен. Но я сомневаюсь, что это будет работать таким же образом при переключении на другой компилятор, если только этот компилятор не попытается имитировать поведение VC.
Это, конечно, только VC++, поскольку я не думаю, что другие компиляторы используют файлы def. Кроме того, если я помню, что кто-то однажды сказал мне, это то, что у Microsoft есть идея интерфейса dll, в которой элементы, выбранные пользователем, открываются публично через файл def или __declspec, тогда как в Unix с их файлами * .so все с общедоступным API отображается. У них нет разницы между логическим публичным API и публичным API библиотеки.
Не существует независимого от языка или версии способа экспорта перегруженной функции, поскольку соглашение об изменении может меняться с каждым выпуском компилятора.
Это одна из причин, почему большинство функций WinXX имеют забавные имена, например * Ex или * 2.
интересный фон с комментарием WinXX!
Официального способа делать то, что вы хотите, не существует, потому что интерфейс dll - это C api.
Сам компилятор использует искаженные имена в качестве обходного пути, поэтому вам следует использовать искажение имен, если вы не хотите слишком много менять в своем коде.
У меня была аналогичная проблема, поэтому я тоже хотел написать об этом.
Обычно используют
extern "C" __declspec(dllexport) void Foo();
экспортировать имя функции можно. обычно экспортирует имя распутать без необходимости .def файл. Однако есть некоторые исключения, такие как функции __stdcall и имена перегруженных функций.
Если вы объявляете функцию для использования __stdcall (как это делается для многих функций API), затем
extern "C" __declspec(dllexport) void __stdcall Foo();
экспортирует искаженное имя, например _Foo @ 4. В этом случае вам может потребоваться явно сопоставить экспортируемое имя к внутреннему искаженному имени.
A. Как экспортировать незапутанное имя. В файле .def добавьте
----
EXPORTS
; Explicit exports can go here
Foo
-----
Это попытается найти «лучшее совпадение» для внутренней функции Foo и экспортировать его. В случае выше, когда есть только one foo это создаст отображение
Foo = _Foo @ 4
как видно через dumpbin / EXPORTS
Если вы перегрузили имя функции, вам может потребоваться явно указать, какую функцию вы хотите в файле .def. путем указания искаженного имени с использованием синтаксиса entryname [= internalname]. например
----
EXPORTS
; Explicit exports can go here
Foo=_Foo@4
-----
B. Альтернативой файлам .def является то, что вы можете экспортировать имена «на месте» с помощью #pragma.
#pragma comment(linker, "/export:Foo=_Foo@4")
C. Третья альтернатива - объявить только одну версию Foo как extern «C», которая будет экспортироваться без перепутывания. Подробнее см. здесь.
Определение Systax для EXPORTS:
entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]
имя записи - это имя функции или переменной, которую вы хотите экспортировать. Это обязательно. Если имя, которое вы экспортируете, отличается от имени в DLL, укажите имя экспорта в DLL с внутренним именем.
Например, если ваша DLL экспортирует функцию func1 () и вы хотите, чтобы она использовалась как func2 (), вы должны указать:
EXPORTS
func2=func1
Просто посмотрите искаженные имена (используя Dependency walker) и укажите свое собственное имя функции.
Источник: http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=vs.71).aspx
Обновлено: это работает для динамических DLL, где нам нужно использовать GetProcAddress () для явного извлечения функций в Dll.
что, если мы используем GetProcAddress () с динамической DLL?