Статическое и динамическое связывание DLL с приложением
Библиотеки DLL могут связываться с приложением двумя путями: статическим связыванием или динамическим связыванием.
При статическом связывании DLL загружается сразу, как только начинает выполняться приложение, которое будет ее использовать. Это наиболее простой способ использования DLL. Вызов функций в приложении при этом почти не отличается от вызова любых других функций. Здесь время загрузки увеличивается и нельзя выполнять приложение, если нет соответствующего файла DLL. Кроме того, без DLL приложение не может выполняться и в сокращенном, также рабочем варианте. Недостатком статического связывания является и то, что DLL занимает память все время, в течение которого выполняется приложение, независимо от того, вызываются ли в данном сеансе работы с приложением какие-то функции библиотеки, или нет.
Статическое связывание подразумевает, что для DLL создан специальный файл описаний импортируемых функций (import library file). Этот файл имеет расширение .lib и то же имя, что и соответствующая DLL, и должен быть связан с приложением на этапе компиляции.
При динамическом связывании DLL загружается только в тот момент, когда необходимо выполнить какую-то хранящуюся в ней функцию. Затем DLL можно выгрузить из памяти. Но при более эффективном использовании памяти вызов функций DLL существенно усложняется, и время вызова увеличивается.
Для вызова библиотечной функции надо сначала загрузить библиотеку функцией LoadLibraryAPI Windows. Затем с помощью функции GetProcAddress надо получить указатель на нужную функцию библиотеки. Только после этого можно выполнять функцию. А затем с помощью функции FreeLibrary надо выгрузить библиотеку из памяти.
Предположим, создана библиотека mydll.dll, содержащая некоторую функцию char* MyFunction(char*). Тогда для загрузки DLL надо выполнить оператор вида:
//загрузка DLL
HINSTANSE dllInstanse =LoadLibrary(“mydll.dll”);
Получить указатель на импортируемую функцию можно следующим кодом:
//получение указателя на функцию
typedef char* (__import FType(char*));
FType * MyFunc;
MyFunc = (FType*)GetProcAddress(dllInstanse, “_MyFunction”);
Объявление typedef вводит пользовательский тип (тип-функция) с произвольным именем FType. Введенный тип используется для задания типа указателя на функцию MyFunc. Для получения значения этого указателя используется функция API Windows GetProcAddress. Она принимает в качестве параметров указатель на загруженный модуль DLL и имя функции, а возвращает указатель на функцию. Этот указатель приводится к типу указателя на используемую функцию библиотеки.
Вызов функции осуществляется с помощью указателя на неё:
//вызов функции
char* S = MyFunc(“Привет!”);
Когда работа с DLL завершена, её можно выгрузить из памяти оператором вида:
//выгрузка DLL
FreeLibrary(dllInstanse);
Если нет острой необходимости использовать именно динамическое связывание, лучше всегда использовать статическое связывание.