Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
основы программирования в Windows.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
88 Кб
Скачать
☆

Int sharedInts[10] ;

// другие переменные общего пользования

#pragma data_seg()

#pragma comment (lib, "msvcrt" "-SECTION:.myseg, rws") ;

Все переменные, объявленные между директивами #pragma data_seg(), размещаются в сегменте .myseg. Директива #pragma comment () - не обычный комментарий. Она дает указание библиотеке выполняющей системы пометить новый раздел как разрешенный для чтения, записи и совместного доступа.

Для демонстрации принципа разработки DLL, создадим простую динамическую библиотеку, содержащую только простую функцию DllMain() и одну внешнюю функцию, которая получает строку символов и выводит ее в виде окна с сообщением.

#include <windows.h>

int WINAPI DllMain (HINSTANCE hInctance, DWORD fdReason, PVOID pvReserved)

{

return TRUE;

}

extern "C" __declspec (dllexport) int MyFunction(char *str)

{

MessageBox (NULL,str,"Function from DLL",MB_OK);

return 1;

}

Компиляция данного проекта приведет к созданию двух необходимых в дальнейшем файлов - с расширениями .dll и .lib.

1.3.4. Подключение DLL

Практически невозможно создать приложение Windows, в котором не использовались бы библиотеки DLL. В DLL содержатся все функции Win32 API и несчетное количество других функций операционных систем Win32. Вообще говоря, DLL - это просто наборы функций, собранные в библиотеки. Однако, в отличие от своих статических родственников (файлов .lib), динамические библиотеки не присоединены непосредственно к выполняемым файлам с помощью редактора связей. В выполняемый файл занесена только информация об их местонахождении. В момент выполнения программы загружается вся библиотека целиком. Благодаря этому разные процессы могут пользоваться совместно одними и теми же библиотеками, находящимися в памяти. Такой подход позволяет сократить объем памяти, необходимый для нескольких приложений, использующих много общих библиотек, а также контролировать размеры ЕХЕ-файлов.

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

В случае использования динамических библиотек, как было сказано ранее, приложение может активизировать размещенные в DLL процедуры двумя методами: поздними неявным и явным связыванием. Рассмотрим их более подробно.

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

  • каталог, в котором находится ЕХЕ-файл;

  • текущий каталог процесса;

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

Если библиотека DLL не обнаружена, приложение выводит диалоговое окно с сообщением о ее отсутствии и путях, по которым осуществлялся поиск. Затем процесс отключается. Если нужная библиотека найдена, она помещается в оперативную память процесса, где и остается до его окончания. Теперь приложение может обращаться к функциям, содержащимся в DLL. Для импорта функций библиотеки необходимо использовать строку __declspec (dllimport).

Эта форма динамического связывания наиболее проста, однако при некоторых условиях она может создавать проблемы. Например, если приложение ссылается на процедуру из DLL таким способом, в случае его реализации DLL-библиотека должна существовать даже тогда, когда приложение никогда не обращается к процедуре. Кроме того, на этапе компиляции приложение должно знать имена всех процедур, которые будут связываться во время загрузки. Для этого используется специальный файл с расширени-ем .lib. Однако .lib-файл, используемый при неявном связывании DLL, — это не обычная статическая библиотека. Такие .lib-файлы называются библиотеками импортирования (import libraries). В них содержится не сам код библиотеки, а только ссылки на все функции, экспортируемые из файла DLL, в котором все и хранится. В результате библиотеки импортирования, как правило, имеют меньший размер, чем DLL-файлы. Создаются такие файлы обычно средой разработки одновременно с .dll. Имя .lib-файла определяется среди прочих параметров редактора связей в командной строке или на вкладке "Link" диалогового окна "Project Settings" среды Developer Studio. В библиотеках языка С имена функций не расширяются, поэтому если необходимо подключить библиотеку на С к приложению на C++, все функции из этой библиотеки придется объявить как внешние в формате С:

extern "С" __declspec (dllimport) int MyOldCFunction(int myParam) ;

Рассмотрим простой пример подключения библиотеки с поздним неявным связыванием. В качестве Dll используем приведенную выше. Исходный код приложения следующий:

#include <windows.h>

extern "C" __declspec (dllimport) int MyFunction (char *str);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{

int iCode=MyFunction ("Hello!");

return 0;

}

2. Позднее явное связывание. Хотя данный тип связывания преодолевает ограничения во время загрузки, это метод требует большой работы над приложением. Вместо указания DLL-процедур на стадии компиляции приложение использует функции LoadLibrary(), LoadLibraryEx(), GetProcAddress() и FreeLibrary(), чтобы задать во время выполнения имена DLL-библиотек и процедур, на которые будут выполнятся ссылки.

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

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

HINSTANCE hMyDll;

if ( (hMyDll=:: LoadLibrary ( "MyDLL") ) ==NULL) { / * не удалось загрузить DLL */ }

else { /* приложение имеет право пользоваться функциями DLL через hMyDll */ }

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

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

Если файл обнаружен и библиотека успешно загрузилась, функция LoadLibrary() возвращает ее дескриптор, который используется для доступа к функциям библиотеки.

Расширенная функция LoadLibraryEx() получает три параметра:

HMODULE (LPCTSTR lpFileName, // имя загружаемого модуля,

HANDLE hFile, // зарезервирован, должен быть нулевым

DWORD dwFlags); // флаги, определяющие опции загрузки модуля

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

// тип PFH_MyFunction будет объявлять указатель на функцию,

// принимающую указатель на символьный буфер и выдающую значение типа int

typedef int (WINAPI *PFH_MyFunction) (char *);

...

PFH_MyFunction pfhMуFunction;

Затем следует получить дескриптор библиотеки, при помощи которого и определить адреса функций, например адрес функции с именем MyFunction:

hMyDll=::LoadLibrary("MyDLL");

pfhMyFunction=(PFH_MyFunction)::GetProcAddress(hMyDll,"MyFunction") ;

...

int iCode=(*pfhMyFunction) ("Hello");

В обращении к функции GetProcAddress() следует указывать имя библиотеки и имя функции. Допускается указание имени процедуры в виде ASCII-строки либо порядкового номера (в этом случае для создания библиотеки должен использоваться def-файл, второй параметр GetProcAddress() приводится к виду MAKEINTRESOURCE(N)). Синтаксис функции GetProcAddress():

FARPROC GetProcAddress(HMODULE hDLLibrary, LPCSTR lpszProcName);

При успешном завершении функция возвращает адрес точки входа запрашиваемой процедуры. В противном случае возвращается NULL. Параметры функции:

hDLLibrary - дескриптор DLL, возвращаемый функцией LoadLibrary();

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

После завершения работы с библиотекой динамической компоновки, ее можно выгрузить из памяти процесса с помощью функции FreeLibrary():

::FreeLibrary(hMyDll);

Рассмотрим часть исходного кода программы подключения DLL с поздним явным связыванием. В качестве динамической библиотеки используем приведенную выше:

…

typedef int (WINAPI *PFH_MyFunction) (char *);

HINSTANCE hMyDll = LoadLibrary("MyDll.dll");

if (hMyDll )

{

PFH_MyFunction pfhMуFunction =

(PFH_MyFunction)GetProcAddress(hMyDll, "MyFunction" );

if (pfhMуFunction )

int iCode = (*pfhMуFunction)( "Hello!" );

else

MessageBox( NULL, "Cannot find function!", NULL,

MB_OK | MB_ICONASTERISK );

FreeLibrary(hMyDll );

}

else

MessageBox( NULL, "DLL could not be loaded", NULL,

MB_OK | MB_ICONASTERISK );