Операционная система Microsoft Windows 3.1 для программиста -том 3

         

Динамический импорт функций во время выполнения приложения


В некоторых случаях невозможно выполнить динамическую компоновку на этапе редактирования. Вы можете, например, создать приложение, которое состоит из основного модуля и дополнительных, реализованных в виде DLL-библиотек. Состав этих дополнительных модулей и имена файлов, содержащих DLL-библиотеки, может изменяться, при этом в приложение могут добавляться новые возможности.

Если вы, например, разрабатываете систему распознавания речи, то можете сделать ее в виде основного приложения и набора DLL-библиотек, по одной библиотеке для каждого национального языка. В продажу система может поступить в комплекте с одной или двумя библиотеками, но в дальнейшем пользователь сможет купить дополнительные библиотеки и, просто переписав новые библиотеки на диск, получить возможность работы с другими языками. При этом основной модуль приложения не может "знать" заранее имена файлов дополнительных DLL-библиотек, поэтому динамическая компоновка с использованием библиотеки импорта или оператора IMPORTS файла определения модуля невозможна.

Однако приложение может в любой момент времени загрузить любую DLL-библиотеку, вызвав специально предназначенную для этого функцию программного интерфейса Windows с именем LoadLibrary . Приведем ее прототип:

HINSTANCE WINAPI LoadLibrary(LPCSTR lpszLibFileName);

Параметр функции является указателем на текстовую строку, закрытую двоичным нулем. В эту строку перед вызовом функции следует записать путь к файлу DLL-библиотеки или имя этого файла. Если путь к файлу не указан, при поиске выполняется последовательный просмотр следующих каталогов:

текущий каталога;

каталог, в котором находится операционная система Windows;

системный каталог Windows;

каталог, в котором находится загрузочный файл приложения, загружающего DLL-библиотеку;

каталоги, перечисленные в операторе описания среды PATH, расположенном в файле autoexec.bat;

каталоги, расположенные в сети, если для них определены пути поиска.

Если файл DLL-библиотеки найден, функция LoadLibrary возвращает идентификатор модуля библиотеки.




В противном случае возвращается код ошибки, который по своему значению меньше величины HINSTANCE_ERROR, определенной в файле windows.h. Возможны следующие коды ошибок:

Код ошибки Описание
0 Мало памяти, неправильный формат загружаемого файла
2 Файл не найден
3 Путь к файлу не существует
5 Была предпринята попытка динамически загрузить приложение или произошла ошибка при совместном использовании файлов. Также возможно, что была предпринята попытка доступа к файлу в сети пользователем, не имеющим на это достаточных прав
6 Данная библиотека требует отдельный сегмент данных для каждой задачи
8 Мало памяти для запуска приложения
10 Неправильная версия операционной системы Windows
11 Неправильный формат загрузочного файла приложения Windows
12 Данное приложение разработано для операционной системы, отличной от Microsoft Windows
13 Данное приложение разработано для MS-DOS версии 4.0
14 Неизвестный тип исполняемого файла
15 Была предпринята попытка загрузить приложение, разработанное для реального режима работы процессора в среде ранних версий операционной системы Windows
16 Была предпринята попытка загрузить вторую копию исполняемого файла, содержащего сегменты данных, отмеченные как multiple, но не защищенные от записи
19 Попытка загрузки компрессованного выполнимого файла
20 Файл, содержащий DLL-библиотеку, имеет неправильный формат
21 Данное приложение работает только в среде 32-битового расширения Windows
Функция LoadLibrary может быть вызвана разными приложениями для одной и той же DLL-библиотеки несколько раз. В этом случае загрузка DLL-библиотеки выполняется только один раз. Последующие вызовы функции LoadLibrary приводят только к увеличению счетчика использования DLL-библиотеки.

В качестве примера приведем фрагмент исходного текста приложения, загружающего DLL-библиотеку из файла srcdll.dll:

HINSTANCE hLib; hLib = LoadLibrary("srcdll.dll"); if(hLib >= HINSTANCE_ERROR) { // Работа с DLL-библиотекой } FreeLibrary(hLib);



Если DLL- библиотека больше не нужна, ее следует освободить с помощью функции FreeLibrary :

void WINAPI FreeLibrary(HINSTANCE hLibrary);

В качестве параметра этой функции следует передать идентификатор освобождаемой библиотеки.

Если счетчик использования DLL-библиотеки становится равным нулю (что происходит, когда все приложения, работавшие с библиотекой, освободили ее или завершили свою работу), DLL-библиотека выгружается из памяти. При этом вызывается функция WEP, выполняющая все необходимые завершающие действия.

Однако прежде чем выгружать библиотеку из памяти, приложение, вероятно, захочет вызвать из нее хотя бы одну функцию, поэтому теперь мы расскажем о том, как вызвать функцию из загруженной DLL-библиотеки.

Для того чтобы вызвать функцию из библиотеки, зная ее идентификатор, необходимо получить значение дальнего указателя на эту функцию, вызвав функцию GetProcAddress :

FARPROC WINAPI GetProcAddress(HINSTANCE hLibrary, LPCSTR lpszProcName);

Через параметр hLibrary вы должны передать функции идентификатор DLL-библиотеки, полученный ранее от функции LoadLibrary.

Параметр lpszProcName является дальним указателем на строку, содержащую имя функции или ее порядковый номер, преобразованный макрокомандой MAKEINTRESOURCE.

Приведем фрагмент кода, в котором определяются адреса двух функций. В первом случае используется имя функции, а во втором - ее порядковый номер:

FARPROC lpMsg; FARPROC lpTellMe; lpMsg = GetProcAddress(hLib, "Msg"); lpTellMe = GetProcAddress(hLib, MAKEINTRESOURCE(8));

Перед тем как передать управление функции по полученному адресу, следует убедиться в том, что этот адрес не равен NULL:

if(lpMsg != (FARPROC)NULL) { (*lpMsg)((LPSTR)"My message"); }

Для того чтобы включить механизм проверки типов передаваемых параметров, вы можете определить свой тип - указатель на функцию, и затем использовать его для преобразования типа адреса, полученного от функции GetProcAddress:

typedef int (FAR PASCAL *LPGETZ)(int x, int y); LPGETZ lpGetZ; lpGetZ = (LPGETZ)GetProcAddress(hLib, "GetZ");



А что произойдет, если приложение при помощи функции LoadLibrary попытается загрузить DLL-библиотеку, которой нет на диске?

В этом случае операционная система Windows выведет на экран диалоговую панель с сообщением о том, что она не может найти нужную DLL-библиотеку. В некоторых случаях появление такого сообщения нежелательно, так как либо вас не устраивает внешний вид этой диалоговой панели, либо по логике работы вашего приложения описанная ситуация является нормальной.

Для того чтобы отключить режим вывода диалоговой панели с сообщением о невозможности загрузки DLL-библиотеки, вы можете использовать функцию SetErrorMode , передав ей в качестве параметра значение SEM_NOOPENFILEERRORBOX .

Приведем прототип функции SetErrorMode:

UINT WINAPI SetErrorMode(UINT fuErrorMode);

Эта функция позволяет отключать встроенный в Windows обработчик прерывания MS-DOS INT24h (критическая ошибка). В качестве параметра этой функции можно указывать комбинацию следующих значений:

Значение Описание
SEM_FAILCRITICALERRORS Операционная система Windows не выводит на экран сообщение обработчика критических ошибок, возвращая приложению соответствующий код ошибки
SEM_NOGPFAULTERRORBOX На экран не выводится сообщение об ошибке защиты памяти. Этот флаг может использоваться только при отладке приложений, если они имеют собственный обработчик такой ошибки
SEM_NOOPENFILEERRORBOX Если Windows не может открыть файл, на экран не выводится диалоговая панель с сообщением об ошибке
Функция SetErrorMode возвращает предыдущий режим обработки ошибки.


Содержание раздела