68899

Динамически подключаемые библиотеки

Лекция

Информатика, кибернетика и программирование

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

Русский

2014-09-26

47 KB

4 чел.

Лекция 12. Динамически подключаемые библиотеки

Типы связывания

До сих пор мы использовали множество функций API для создания окон и оконных процедур, рисования, работы с клавиатурой и мышью, ввода-вывода. Все эти функции работали исправно и вы не задумывались над вопросом: где расположены эти функции и каким образом они подключаются к вашей программе. Все, что вам необходимо было сделать, это подключить соответствующий заголовочный файл (чаще всего это был <windows.h>) и вызвать необходимую функцию. Все остальные действия по включению функции в вашу программу среда программирования делала автоматически. Теперь пришла пора рассмотреть механизмы, обеспечивающие возможность использования функций.

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

#include "iostream.h"

// Функция вычисления факториала

int NFactorial(int N)

{    

if (N==1) return 1

                    else  return NFactorial(N-1) * N;

}

// Основная программа

int main()

{

cout<<NFactorial(3)<<endl;

return 0;

}

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

     Файл factorial.cpp Файл factorial.obj Файл factorial.exe

Рис.12.1 Компиляция программы со статическим связыванием первого вида

Приведенная на рисунке 12.1 схема отображает процесс статического связывания первого вида, когда функция расположена в самом тексте программы, при компиляции переводится в объектный код и помещается вместе с объектным кодом основной программы в один файл, который носит название объектного файла (*.obj). После этого, компоновщик (программа связи) создает *.ехе файл, в который включены все объектные части программы и эти части связаны между собой посредством адресации. Вы можете вынести функцию в отдельный текстовый файл, включив его в проект и объявив в файле-заголовке *.h.

Статическое связывание второго вида подразумевает использование в вашей программе функций, определенных в других файлах (библиотеках). Файлы-библиотеки как правило имеют расширение *.lib и подключаются к вашему исполняемому файлу (*.exe)  только на этапе компиляции и связывания (Рис.2). Таким образом, они не компилируются заново, их объектный код (аналог файла *.obj) уже существует, и прикомпилируется к вашему файлу (*.obj) во время компиляции. Единственное, что необходимо сделать, это подключить нужный файл –заголовок (*.h) в текст программы.

     Файл factorial.cpp Файл factorial.obj Файл factorial.exe

Рис.12.2 Компиляция программы со статическим связыванием второго вида

И, наконец, вы имеете возможность использовать динамически подключаемые библиотеки (*.dll). Функции, находящиеся в них подключаются к вашему исполняемому файлу (*.exe) только в момент вызова, то есть в тот момент, когда программа выполняется и идет обращение к указанной функции. Таким образом, функция не компилируется вместе с вашей программой, не участвует в процессе связывания и не содержится в вашем *.exe файле!!! Такой подход имеет неоспоримые преимущества:

  1.  Часто используемые функции хранятся в отдельных файлах. Например все функции API реализованы в DLL и поставляются вместе с операционной системой. Таким образом, все программы под Windows имеют возможность использовать одни и те же функции.
  2.  Нет необходимости помещать все функции программы в *.exe файл. Их можно подгружать по мере надобности.
  3.  Возможность использования новых версий функций (dll-файлов) без перекомпиляции исполняемых модулей (exe-файлов).

Рассмотрим процесс создания и использования библиотек DLL.

Процесс создания динамической библиотеки проходит в два этапа. Первый из них заключается в создании файла-заголовка (*.h), который определяет основные свойства проекта.

С помощью "визарда" среды Microsoft Visual C++ создайте новый проект, выбрав в качестве типа проекта Dinamic Linked Library , после чего определите "пустой проект" (empty project). Теперь вам предстоит самостоятельно создать файлы проекта. Пусть имя вашего проекта будет funlib.

Создайте файл заголовка – funlib.h (при помощи меню File\New..., выбрав файл-заголовок ). В этом файле наьерите следующий текст:

// File funlib.h

#define EXPORT extern "C" __declspec(dllexport)

EXPORT BOOL CALLBACK return333();

EXPORT int CALLBACK MyInc(int i);

Первая строка файла определяет некоторую директиву EXPORT, которая будет использоваться для подключения сервисов, необходимых для создания ссылок на экспортируемые функции (то есть функции, которые могут быть использованы или импортированы другими программами). Следующие две строки являются предварительным объявлением функций, директива EXPORT и делает их динамическими функциями.

Теперь осталось написать обычный текст программы *.cpp, который мало чем  отличается от обычного:

//funlib.cpp

#include <windows.h>

#include <string.h>

#include "fulib.h"

int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)

{

          return TRUE;

}

EXPORT BOOL CALLBACK EdrCenterText()

{

return 333;

}

EXPORT int CALLBACK MyInc(int i)

{

return ++i;

}

Незнакомой здесь является только функция DllMain, которая является аналогом функции main для консольных приложений и функции WinMain для приложений, написанный под Windows. Эта функция автоматически вызывается при загрузке любой dll. Возвращаемое значение TRUE свидетельствует об успешной загрузке и инициализации внутренних ресурсов. Остальные две функции являются обычными функциями пользователя, а директива EXPORT уже нам знакома. Все. Компилируем проект и dll готова!

Настала пора ее использования. Для этого создадим обычное консольное приложение Win32 и назовем его usedll. Для использования funlib.dll необходимо выполнить следующие: поместить в каталог проекта файлы funlib.h и funlib.lib. Второй файл является результатом компиляции предыдущего проекта и его можно найти в каталоге Debug. Кроме этого, в меню Project\Settings\Link, Категория General, поле Object\library modules необходимо вписать имя файла-библиотеки для организации настроек связывания. Текст программы usedll.cpp приведен ниже.

// usedll.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#include "iostream.h"

#include "funlib.h"

int main(int argc, char* argv[])

{

cout<<return333()<<endl;

cout<<MyInc(5)<<endl;

return 0;

}

Данная программа выполнится если в каталоге, где располагается файл usedll.exe расположен файл динамической библиотеки funlib.dll. В результате выполнения данной программы на экране вы увидите следующую картину:

333

6

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

Второй способ использования DLL

Рассмотренный выше способ написания программы, использующей динамически подключаемые библиотеки пригоден только в том случае, когда вы сами создали библиотеки, или, по крайней мере, вы имеете в наличии файл библиотеки *.lib. Однако, очень часто случается так, что есть только *.dll (или она только планируется). В этом случае можно использовать другой способ, который не требует для компиляции проекта наличия файлов, связанных с вызываемой *.dll.

Применение данного способа заключается в следующем:

  1.  Создаются прототипы функций, имеющие те же параметры, что и необходимые функции внутри *.dll. Для нашего примера это будет выглядеть так:

typedef  BOOL  (WINAPI * Inc)(int i);

  1.  Объявляется переменная типа указателя на данную функцию:

Inc pI;

  1.  В программе осуществляется загрузка библиотеки:

HINSTANCE hL;

hL=LoadLibrary("funlib.dll");

  1.  объявленному указателю на функцию присваивается адрес функции из *.dll:

pI=(Inc)GetProcAddress(hL,(LPCSTR)2);

  1.  Функция используется по назначению:

cout<<pI(6);

  1.  После использования выгружается из памяти:

FreeLibrary(hL);

Целиком рассмотренный пример выглядит следующим образом:

#include "stdafx.h"

#include "iostream.h"

#include "windows.h"

HINSTANCE hL;

typedef int (CALLBACK * Inc)(int i);

Inc pI;

int main(int argc, char* argv[])

{   hL=LoadLibrary("1111.dll");

   pI=(Inc)GetProcAddress(hL,(LPCSTR)2);

   cout<<pI(6);

FreeLibrary(hL);

return 0;

}

Наличие *.dll требуется только во время запуска  *.exe файла.


 

А также другие работы, которые могут Вас заинтересовать

83896. Хирургическая анатомия толстой кишки. Отделы, кровоснабжение, венозный отток. Боковые каналы 50.73 KB
  Отделы толстой кишки: Слепая кишка Восходящая ободочная кишка Правый изгиб ободочной кишки Поперечная ободочная кишка Левый изгиб ободочной кишки Нисходящая ободочная кишка Сигмовидная ободочная кишка Прямая кишка Кровоснабжение ободочной кишки осуществляется верхней и нижней брыжеечными артериями. Ветви верхней брыжеечной артерии: Подвздошноободочная артерия отдает ветви к терминальному отделу подвздошной кишки червеобразному отростку передние и задние слепокишечные артерии и восходящую артерию кровоснабжающую начальную...
83897. Хирургическая анатомия слепой кишки. Техника выполнения аппендэктомии при ретроперитонеальном расположении червеобразного отростка 50.91 KB
  Техника выполнения аппендэктомии при ретроперитонеальном расположении червеобразного отростка. Червеобразный отросток Варианты положения периферической части отростка нисходящее верхушка отростка обращена вниз и влево и достигает пограничной линии а иногда опускается в малый таз наиболее частый вариант; медиальное вдоль концевого отдела подвздошной кишки; латеральное в правом боковом канале; восходящее вдоль передней стенки слепой кишки; ретроцекальное и ретроперитонеальное в забрюшинной клетчатке. Проекция основания отростка...
83898. Аппендэктомия. Доступ, техника выполнения, особенности операции при перитоните и гангренозном аппендиците 53.03 KB
  Аппендэктомия ppendectomi удаление червеобразного отростка. Показания: острые или хронические воспалительные изменения червеобразного отростка доброкачественные и злокачественные его новообразования. Оперативный прием При пересечении брыжейки отростка порциями со стороны свободного ее конца накладывают кровоостанавливающий зажим ближе к основанию пересекают брыжейку над зажимом после чего часть брыжейки под зажимом прошивают лигатуру завязывают. Культя отростка погружается в кисет.
83899. Ретроградная аппендэктомия. Доступ, показания, техника выполнения, опасности и профилактика осложнений 46.28 KB
  Показания: спаечный процесс в области червеобразного отростка ретроцекальное или ретроперитонеальное его положение невозможно вывести отросток в рану. Технические приемы: Отыскивание начального отдела слепой кишки и отростка. Проделывание окна в брыжейке отростка у его основания перевязка отростка. Пересечение отростка погружение культи в стенку слепой кишки по описанному выше способу.
83900. Хирургическое лечение рака толстой кишки 49.17 KB
  Радикальное иссечение опухоли тослтой кишки вместе с соответствующей частью брыжейки с сосудами и сопровождающими лимфатическими сосудами и узлами является наиболее подходящей операцией для локального устранения опухоли. Виды резекции толстой кишки в зависимости от локализации патологического процесса: Правосторонняя гемиколэктомия удаление всей правой половины толстой кишки захватывая 1015 см конечного отрезка подвздошной кишки слепую восходящую ободочную правый изгиб и правую треть поперечной ободочной кишки с последующим наложением...
83901. Операция Гартмана. Показания, техника выполнения 50.66 KB
  Операция заключается в одномоментной резекции пораженного отрезка сигмовидной ободочной и части прямой кишки с наложением одноствольного противоестественного заднего прохода. Показания: операция показана у ослабленных и пожилых больных при раке сигмовидной ободочной кишки или ректосигмоидного отдела осложненном непроходимостью или перфорацией а также при завороте сигмовидной ободочной кишки с гангреной ее и перитонитом. После ревизии брюшной полости производят мобилизацию сигмовидной ободочной кишки а при раке ректосигмовидного отдела...
83902. Хирургическая анатомия прямой кишки. Хирургическое лечение геморроя 50.85 KB
  Хирургическая анатомия прямой кишки Скелетотопия: начало соответствует уровню верхнего края S2 позвонка. Строение: В зависимости от местоположения кишки в ней выделяют тазовую лежит выше диафрагмы и содержит надампулярную часть и ампулу и промежностную анальный канал части. Покрытие брюшиной: надампулярный отдел прямой кишки покрыт брюшиной интраперитонеально в области ампулы брюшина покрывает переднюю и частично боковые стенки кишки переходя на матку у мужчин на мочевой пузырь и на боковые стенки таза.
83903. Виды операций на прямой кишке 48.36 KB
  Сфинктеросохраняющие операции: передняя резекция прямой кишки; брюшноанальная резекция прямой кишки с низведением Сфинктеронесохраняющие операции связанные с удалением замыкательного аппарата и наложением противоестественного заднего прохода: брюшнопромежностная экстирпация прямой кишки; обструктивная резекция прямой кишки. Передняя резекция прямой кишки показана при раке верхнеампулярного и ректосигмоидного отделов нижняя граница опухоли располагается на 10 см выше прямокишечнозаднепроходной линии. Брюшноанальную резекцию прямой...
83904. Операции наложения противоестественного заднего прохода 45.26 KB
  Показания: опухоли раны рубцовые сужения прямой кишки ампутации прямой кишки. Техника наложения одноствольного противоестественного заднего прохода операция Гартмана: послойное вскрытие брюшной полости косым переменным разрезом в левой паховой области; прокалывание брыжейки кишки в бессосудистой зоне и проведение через окно резиновой трубки; сшивание под трубкой приводящей и отводящей петель между собой 34 узловыми серозномышечными швами образование шпоры; подшивание париетальной брюшины к краям кожного разреза; подшивание...