Книга: Системное программирование в среде Windows
Явное связывание
Явное связывание
Явное связывание, или связывание во время выполнения (run-time linking), требует, чтобы в программе содержались конкретные указания относительно того, когда именно необходимо загрузить или освободить библиотеку DLL. Далее программа получает адрес запрошенной точки входа и использует этот адрес в качестве указателя при вызове функции. В вызывающей программе функция не объявляется; вместо этого в качестве указателя на функцию объявляется переменная. Поэтому во время компоновки программы присутствие библиотеки не является обязательным. Для выполнения необходимых операций требуются три функции: LoadLibrary (или LoadLibraryEx), GetProcAddress и FreeLibrary. На 16-битовое происхождение определений функций указывает присутствие в них дальних (far) указателей и дескрипторов различных типов.
Для загрузки библиотеки служат две функции: LoadLibrary и LoadLibraryEx.
HINSTANCE LoadLibrary(LPCTSTR lpLibFileName)
HINSTANCE LoadLibraryEx(LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
В обоих случаях значением возвращаемого дескриптора (типа HINSTANCE, а не HANDLE) в случае ошибки будет NULL. Суффикс .DLL в имени файла указывать не обязательно. С помощью функций LoadLibrary можно загружать также .ЕХЕ-файлы. При указании путей доступа должны использоваться символы обратной косой черты (); символы прямой косой черты (/) в данном случае работать не будут.
Поскольку библиотеки DLL являются совместно используемым ресурсом, системой поддерживается счетчик ссылок на каждую DLL (который увеличивается на единицу при каждом вызове любой из указанных выше функций загрузки), так что повторное отображение фактического файла библиотеки не требуется. Функция LoadLibrary завершится с ошибкой даже в случае успешного нахождения .DLL-файла, если данная библиотека DLL неявно связана с другой DLL, найти которую программе не удалось.
Функция LoadLibraryEx аналогична функции LoadLibrary, однако имеет несколько флагов, которые оказываются полезными для указания альтернативных путей поиска и загрузки библиотек в виде файла данных. Параметр hFile зарезервирован для использования в будущем. Параметр dwFlags позволяет определять различные варианты поведения системы путем указания одного из трех значений:
1. LOAD_WITH_ALTERED_SEARCH_PATH: отменяет ранее описанный стандартный порядок просмотра каталогов при поиске, изменяя лишь первый из шагов стратегии поиска. Вместо каталога, из которого загружалось приложение, используется путь поиска, указанный в имени lpLibFileName.
2. LOAD_LIBRARY_AS_DATAFILE: файл воспринимается как файл данных и не требует выполнения каких-либо действий по его подготовке к запуску, на пример вызова функции DllMain (см. раздел "Точки входа библиотеки DLL" далее в этой главе).
3. DONT_RESOLVE_DLL_REFERENCE: функция DllMain для инициализаций процессов и потоков не вызывается; загрузка дополнительных модулей, на которые имеются ссылки в указанной DLL, также не производится.
Закончив работать с экземпляром DLL — возможно, с намерением загрузить другую ее версию — вы должны освободить дескриптор библиотеки, тем самым освобождая ресурсы, в том числе распределенное для библиотеки виртуальное адресное пространство. Однако DLL продолжает оставаться загруженной, если счетчик ссылок указывает на то, что она все еще используется другими процессами.
BOOL FreeLibrary(HINSTANCE hLibModule)
После загрузки библиотеки, но до ее освобождения, вы можете получить адрес любой точки входа, используя функцию GetProcAddress.
FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
Параметр hModule, несмотря на другой тип имени (HINSTANCE определен как HMODULE), является экземпляром (instance) библиотеки, получаемым посредством вызова функции LoadLibrary или GetModuleHandle (см. следующий абзац). lpProcName — указатель на строку, содержащую имя точки входа; это имя не может задаваться в кодировке Unicode. В случае неуспешного выполнения функция возвращает значение NULL. Слово FARPROC, означающее "длинный указатель на процедуру", является анахронизмом.
Имя файла, связанного с дескриптором hHandle, можно получить с помощью функции GetModuleFileName. Возможно и обратное: для заданного имени файла (.DLL или .EXE) функция GetModuleHandle в случае успешного выполнения возвратит дескриптор, связанный с этим файлом, если текущий процесс загрузил его.
В следующем примере показано, как использовать адрес точки входа для вызова функции.