Поделиться через


Импорт вызовов функций с помощью __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.

См. также

Импорт в приложение