Имена функций в подцепляемой DLL.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

12 июл 2007, 10:58

Возник такой вопрос. Не то, чтобы критично, но любопытно.

Скажем, пишу я (на C++) некую EXPORT.DLL, которая экспортинует некую функцию, скажем, для простоты:

void ExpFunc (long i);

Но, как мы знаем, C-компилятор на самом деле формирует "внутреннее" имя функции, добавляя спереди подчеркивание ('_'), а сзади - постфикс, в котором зашифрованы типы параметров. От постфикса, конечно, можно избавиться, указав в описании функци, что она - extern "C", но '_' все равно остается (да и по-моему, небольшой постфикс все равно тоже - типа числа, равного суммарному размеру стека, отводимого под параметры).

Но мы люди ушлые, с помощью DEF-файла указываем "красивое" внешнее имя для экспортируемой функции, примерно так:

Код: Выделить всё

EXPORTS
   ExpFunc  =  _ExpFunc@4
Таким образом, при доступе из внешних программ функция будет доступна под именем ExpFunc.


Это, как говорится, присказка была.
А теперь сказка.

А как мне вызывать эту функцию из другой, написанной на C++ же программы?
Имеется в виду "статический" вызов, а не вызов через LoadLibrary / GetProcAddr.
То есть, если я опишу эту импортируемую функцию как
void ExpFunc (long i);
то компилятор же и будет искать ее в EXPORT.LIB под именем с '_' и постфиксом. А она-то проэкспортирована как ExpFunc!

Собственно, вопрос. Как (и возможно ли) при линковке функции из внешней DLL задать "экспортное" имя это функции? Например, в Delphi при созданиии юнита-переходника явно указывается имя DLL и "внешнее" имя экспортируемой функции. А в C++ такое возможно?

Заранее спасибо.
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

12 июл 2007, 15:58

Если ты используешь библиотеку импорта (.LIB) для неявной загрузки DLL из приложения на С++, то тебе какая разница, под каким именем функция экспортирована, с префиксом оно или без него? Для этого нужно декларировать функцию с модификатором __declspec( dllexport ) для DLL и __declspec( dllimport ) для приложения. А экспортное имя удобно использовать для поиска адреса функции с помощью GetProcAddr(). Borland Pascal и Visual Basic не используют библиотеки импорта, поэтому они находят функции только по их именам.
BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

12 июл 2007, 16:09

WinMain,
ну так ведь при попытке слинковать программу/DLL, которая пытается использовать функцию из EXPORT.DLL, я получаю ошибку линкера:

LNK2001: unresolved external symbol __imp__ExpFunc@4

Т.е. я так понимаю, что он ищет в EXPORT.LIB именно это имя, а там лежит __imp_ExpFunc (т.е. без "начального" подчеркивания и без постфикса)
Если ты используешь библиотеку импорта (.LIB) для неявной загрузки DLL из приложения на С++...
Не совсем понял, что подразумевается под НЕЯВНОЙ загрузкой. Я хотел залинковать статически, именно с использованием EXPORT.LIB.
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

12 июл 2007, 17:01

Попробуй задекларировать ее так:

Код: Выделить всё

extern "C" {
  void ExpFunc (long i);
};

Тогда компилятор манглить ее не будет
2B OR NOT(2B) = FF
BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

12 июл 2007, 17:18

Absurd писал(а):Попробуй задекларировать ее так:

Код: Выделить всё

extern "C" {
  void ExpFunc (long i);
};

Тогда компилятор манглить ее не будет
Эээ... сорри, что значит "манглить"?

Еще раз уточняю. В EXPORT.DLL фанкия проэкспортирована именно под именем ExpFunc при помощи директивы DEF-файла

Код: Выделить всё

EXPORTS
   ExpFunc  =  _ExpFunc@4
То есть задачи не изменить как-то это проэкспортированное имя (внося изменения в EXPORT.DLL), а при имеющихся EXPORT.DLL/EXPORT.LIB прилинковать ее (с таким именем) к другому EXE/DLL, который создается также на MS VC++.


Если в вызывающей EXE/DLL описываю ее как:

Код: Выделить всё

extern "C" {
  void __declspec(dllimport) WINAPI ExpFunc (long i);
};
то получаю шибку линкера (см.также выше):
LNK2001: unresolved external symbol __imp__ExpFunc@4
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

12 июл 2007, 17:45

BBB, ты меня не понял.
Неявная загрузка - это и есть загрузка DLL через библиотеку импорта, а не из программного кода через LoadLibrary(). Т.е. загрузка динамической библиотеки и установка связи с её функциями происходит при запуске самого приложения.
Когда ты используешь модификаторы __declspec( dllexport ) для DLL и __declspec( dllimport ) для приложения, то нет необходимости использовать файл .DEF и модификатор extern "C". Этот механизм динамической линковки и декларирования функций как раз предусмотрен именно для приложений на С++.
BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

12 июл 2007, 17:53

WinMain,
Я понимаю, что из EXPORT.DLL можно проэкспортировать функцию без DEF-файла (с extern "C" или без - не существенно). Тогда она попадет в LIB/DLL файл с префиксом/постфиксом, притяным для C++, и при линковке вызывающей программой в LIB-е будет найдена.
Это-то все ясно.
Но как, говорится, тут дело принципа. А именно (как я уже писал), вопрос в том, что если ExpFunc проэкспортирована именно так, как описано (через DEF-файл), то возможно ли в этом случае исхитриться из другой C++ программы/DLL сделать ее вызов через статическую линковкку EXPORT.DLL? Можно ли как-то линкеру указать (к это возможно, например, в Delphi), что "вот эту функцию [ExpFunc] ищи в EXPORT.LIB под именем <таким-то> (т.е. в нашем примере - ExpFunc без всяких "собачек и подчеркиваний")"
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

12 июл 2007, 17:59

Да ну нафиг - функции из DLL надо экспортировать как Pure С - функции с помощью extern "C" и DEF файла. __declspec не нужен. Тогда будет цепляться откуда угодно.

Если нужно экспортировать не функции а классы тогда неплохо бы использовать COM чтобы не быть замкнутым на MSVC.
2B OR NOT(2B) = FF
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

13 июл 2007, 00:48

Можно ли как-то линкеру указать (как это возможно, например, в Delphi), что "вот эту функцию [ExpFunc] ищи в EXPORT.LIB под именем <таким-то> (т.е. в нашем примере - ExpFunc без всяких "собачек и подчеркиваний")"
Нельзя. В Delphi ты декларируешь функцию, которая находится в модуле EXPORT.DLL, а не в EXPORT.LIB
Export.lib по сути является переходником между DLL-модулем и приложением. А то что префикс добавляется к имени функции - то это особенность работы компилятора от Microsoft, и ничего ты с этим не поделаешь.
В С++ Builder есть утилита implib.exe, которая генерит .LIB-файлы для динамических библиотек. В ней есть опция, которая убирает префиксы у функций для модулей, скомпилированных в MSVC.
BBB
Сообщения: 1272
Зарегистрирован: 27 дек 2005, 13:37

13 июл 2007, 10:44

WinMain,
Спасибо.

Хорошо, тогда спросим по другому. Наченем опять с примера. Есть системная виндовая user32.dll. Если поискать в ней контестно, то там найдутся (для примера) ф-ии CreateWindowExA и CreateWindowExW.
В то же время, если контекстно поискать в user32.lib (которая в составе MS Visual C++), то там будет обнаружены строки _CreateWindowExA@48 и __imp__CreateWindowExA@48.

Т.е. получается, что в LIB-файл попало имя с префиксами/постфиксами, но в самой DLL функция лежит без них.
Вопросы:
Как это им удалось?
Могу ли я как-то собрать свою EXPORT.DLL, чтобы через GetProcAddress я мог бы обращаться к функции по имени ExpFunc, в в LIB-файл функция попала так, что он была бы "видна" для линкера MS VC?
Ответить