Импорт вызовов функций с помощью __declspec(dllimport)
Следующий пример кода демонстрирует способ использования _declspec(dllimport) для импорта вызовов функций из библиотеки DLL в приложение.Предположим, что func1 это функция, которая хранится в библиотеке DLL отдельно от EXE-файла, содержащего функцию main.
Без __declspec(dllimport) с данным кодом:
int main(void)
{
func1();
}
компилятор формирует код, имеющий следующий вид:
call func1
а компоновщик, в свою очередь, преобразует вызов подобным образом:
call 0x4000000 ; The address of 'func1'.
Если func1 хранится в другой библиотеке DLL, компоновщик не может ее распознать, поскольку отсутствует информация об адресе func1.В 16-битных средах компоновщик добавляет адрес этого кода в список в EXE-файле, который во время выполнения будет заполнен загрузчиком верными адресами.В 32-битных и 64-битных средах компоновщик производит преобразователь, адрес которого неизвестен.В 32-битной среде преобразователь выглядит следующим образом:
0x40000000: jmp DWORD PTR __imp_func1
В данном примере imp_func1 это адрес для ячейки func1 в таблице импортируемых адресов EXE-файла.Таким образом все адреса распознаются компоновщиком.Для правильной работы загрузчику необходимо только обновить таблицу импортируемых адресов EXE-файла во время загрузки.
Поэтому рекомендуется использовать __declspec(dllimport), поскольку в этом случае компоновщику не приходится создавать преобразователь без надобности.Преобразователи расширяют код (в системах RISC могут быть дополнительные инструкции) и могут ухудшить эффективность кэша.Если компилятор получает данные о том, что функция размещается в библиотеке DLL, он может создать непрямой вызов.
Таким образом, теперь следующий код:
__declspec(dllimport) void func1(void);
int main(void)
{
func1();
}
создает следующую инструкцию:
call DWORD PTR __imp_func1
В этом случае отсутствует преобразователь и инструкция jmp, что укорачивает код и делает его быстрее.
С другой стороны для вызовов функций внутри DLL нет необходимости использовать непрямой вызов.Адрес функции уже известен.Так как чтобы загрузить и хранить адрес функции перед непрямым вызовом требуется время и место, прямой вызов всегда бывает быстрее и компактнее.Рекомендуется использовать __declspec(dllimport) при вызове функций библиотеки DLL, размещенных не в самой библиотеке.Однако не используйте __declspec(dllimport) для функций, размещенных в библиотеке DLL, при построении данной библиотеки.