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