На этом шаге мы рассмотрим динамическую загрузку DLL.
В отличие от неявной (статической) загрузки модуля DLL, когда имена функций импортируемых функций и процедур, а также библиотека должны быть определены на этапе формирования исходного текста приложения и его компиляции, использование явной (динамической) загрузки позволяет задавать имя динамической библиотеки и имя функции непосредственно перед ее выполнением. Для явной загрузки модулей DLL необходимо загрузить модуль DLL в адресное пространство процесса, получить точку входа для нужной функции, выполнить внешнюю функцию и освободить память процесса от модуля. Для этого используются следующие процедуры и функции API Win32: LoadLibrary и LoadLibraryEx, GetProcAddress и FreeLibrary.
Для сравнения явной загрузки модуля с неявной загрузкой рассмотрим уже знакомый пример заполнения почтового конверта. Для использования импортируемых функций зададим их типы и на их основании определим переменные, которые будут использованы как точки входа в процессе выполнения приложения.
type TCreateWord = function:boolean; TVisibleWord = function(kod:boolean):boolean; TAddDoc = function(name:string):variant; TFindAllAndPasteTextDoc = function(document:variant; findtext,pastetext:string):boolean; TOpenDoc = function(filename:string):variant; TCopyTextDocToClipboard = function(document:variant):boolean; TImportTextFromDoc = function(document:variant):string; TCloseDocEx = function(document:variant;saved:boolean):boolean; TCloseWord = function:boolean;
Задав типы импортируемых из модуля DLL функций, переходим к определению переменных, которые будут служить точками входа. Необходимо также определить переменную, которую будем использовать для обращения к модулю.
var
hdll:thandle;
CreateWord_DLL2:TCreateWord;
VisibleWord_DLL2:TVisibleWord;
AddDoc_DLL2:TAddDoc;
FindAllAndPasteTextDoc_DLL2:TFindAllAndPasteTextDoc;
OpenDoc_DLL2:TOpenDoc;
CopyTextDocToClipboard_DLL2:TCopyTextDocToClipboard;
ImportTextFromDoc_DLL2:TImportTextFromDoc;
CloseDocEx_DLL2:TCloseDocEx;
CloseWord_DLL2:TCloseWord;
Загружаем модуль с помощью функции LoadLibrary, которая в случае успеха возвращает указатель на загруженный модуль.
procedure TForm1.Button2Click(Sender: TObject); //Загрузка модуля begin hdll:=LoadLibrary('dserver.dll'); end;
Переходим непосредственно к выполнению функций модуля, которые создадут и заполнят конверт на основе заготовленного шаблона.
Сначала определим точки входа для используемых процедур с помощью функции GetProcAddress, аргументами которой являются указатель на загруженный модуль и строка с именем функции, импортируемой из модуля. После инициализации функций выполняем их (исходный текст в этой части программы не отличается от исходного текста при использовании неявной загрузки модуля).
procedure TForm1.Button1Click(Sender: TObject); var document: variant; begin //Определяем точки входа функций CreateWord_DLL2:=GetProcAddress(hdll, 'CreateWord'); VisibleWord_DLL2:=GetProcAddress(hdll, 'VisibleWord'); AddDoc_DLL2:=GetProcAddress(hdll, 'AddDoc'); FindAllAndPasteTextDoc_DLL2:=GetProcAddress(hdll, 'FindAllAndPasteTextDoc'); OpenDoc_DLL2:=GetProcAddress(hdll, 'OpenDoc'); CopyTextDocToClipboard_DLL2:=GetProcAddress(hdll, 'CopyTextDocToClipboard'); ImportTextFromDoc_DLL2:=GetProcAddress(hdll, 'ImportTextFromDoc'); CloseDocEx_DLL2:=GetProcAddress(hdll, 'CloseDocEx'); CloseWord_DLL2:=GetProcAddress(hdll, 'CloseWord'); if Not CreateWord_DLL2 Then exit; VisibleWord_DLL2(True); // Создаем новый документ по шаблону document:=AddDoc_DLL2(ExtractFileDir(Application.ExeName)+ '\Конверт.dot'); MessageBox(handle,'Шаблон почтового конверта создан!', 'Внимание!',0); // Подставляем адрес FindAllAndPasteTextDoc_DLL2(document,'###индекс&','350049'); FindAllAndPasteTextDoc_DLL2(document,'###адрес&', 'Краснодар, ул. Севастопольская, д. 3, кв. 123'); FindAllAndPasteTextDoc_DLL2(document,'###Получатель&', 'Иванов Иван Иванович'); // Обратный адрес FindAllAndPasteTextDoc_DLL2(document, '###обратный индекс&', '198005'); FindAllAndPasteTextDoc_DLL2(document,'###обратный адрес&', 'Санкт-Петербург, Измайловский пр., д. 29, кв. 111'); FindAllAndPasteTextDoc_DLL2(document,'###отправитель&', 'Петрова Светлана Ивановна'); end;
Результат выполнения процедуры представлен на рисунке 1. Этот результат ничем не отличается от результата использования неявной загрузки модуля или использования обычной библиотеки, подключаемой к создаваемому проекту.
Рис.1. Результат работы приложения
После выполнения функций можно освободить память процесса и выгрузить динамический модуль, если в нем больше нет необходимости.
procedure TForm1.Button3Click(Sender: TObject); //Выгрузка модуля begin FreeLibrary (hdll); end;
Приведем полный текст приложения.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} uses MSWORD; type TCreateWord = function:boolean; TVisibleWord = function(kod:boolean):boolean; TAddDoc = function(name:string):variant; TFindAllAndPasteTextDoc = function(document:variant; findtext,pastetext:string):boolean; TOpenDoc = function(filename:string):variant; TCopyTextDocToClipboard = function(document:variant):boolean; TImportTextFromDoc = function(document:variant):string; TCloseDocEx = function(document:variant;saved:boolean):boolean; TCloseWord = function:boolean; var hdll:thandle; CreateWord_DLL2:TCreateWord; VisibleWord_DLL2:TVisibleWord; AddDoc_DLL2:TAddDoc; FindAllAndPasteTextDoc_DLL2:TFindAllAndPasteTextDoc; OpenDoc_DLL2:TOpenDoc; CopyTextDocToClipboard_DLL2:TCopyTextDocToClipboard; ImportTextFromDoc_DLL2:TImportTextFromDoc; CloseDocEx_DLL2:TCloseDocEx; CloseWord_DLL2:TCloseWord; procedure TForm1.Button1Click(Sender: TObject); var document: variant; begin //Определяем точки входа функций CreateWord_DLL2:=GetProcAddress(hdll, 'CreateWord'); VisibleWord_DLL2:=GetProcAddress(hdll, 'VisibleWord'); AddDoc_DLL2:=GetProcAddress(hdll, 'AddDoc'); FindAllAndPasteTextDoc_DLL2:=GetProcAddress(hdll, 'FindAllAndPasteTextDoc'); OpenDoc_DLL2:=GetProcAddress(hdll, 'OpenDoc'); CopyTextDocToClipboard_DLL2:=GetProcAddress(hdll, 'CopyTextDocToClipboard'); ImportTextFromDoc_DLL2:=GetProcAddress(hdll, 'ImportTextFromDoc'); CloseDocEx_DLL2:=GetProcAddress(hdll, 'CloseDocEx'); CloseWord_DLL2:=GetProcAddress(hdll, 'CloseWord'); if Not CreateWord_DLL2 Then exit; VisibleWord_DLL2(True); // Создаем новый документ по шаблону document:=AddDoc_DLL2(ExtractFileDir(Application.ExeName)+ '\Конверт.dot'); MessageBox(handle,'Шаблон почтового конверта создан!', 'Внимание!',0); // Подставляем адрес FindAllAndPasteTextDoc_DLL2(document,'###индекс&','350049'); FindAllAndPasteTextDoc_DLL2(document,'###адрес&', 'Краснодар, ул. Севастопольская, д. 3, кв. 123'); FindAllAndPasteTextDoc_DLL2(document,'###Получатель&', 'Иванов Иван Иванович'); // Обратный адрес FindAllAndPasteTextDoc_DLL2(document, '###обратный индекс&', '198005'); FindAllAndPasteTextDoc_DLL2(document,'###обратный адрес&', 'Санкт-Петербург, Измайловский пр., д. 29, кв. 111'); FindAllAndPasteTextDoc_DLL2(document,'###отправитель&', 'Петрова Светлана Ивановна'); end; procedure TForm1.Button2Click(Sender: TObject); //Загрузка модуля begin hdll:=LoadLibrary('dserver.dll'); end; procedure TForm1.Button3Click(Sender: TObject); //Выгрузка модуля begin FreeLibrary (hdll); end; end.
На предыдущих шагах мы рассмотрели создание и использование DLL для работы с документами MS Word в приложениях Delphi. Справедливости ради стоит отметить, что и сами приложения MS Office могут работать с внешними динамическими модулями. Эта возможность очень заманчива и может дать большой положительный эффект при создании и использовании приложений на любом языке, позволяющем создавать динамические библиотеки. Следующие шаги будут посвящены рассмотрению этого вопроса.
Со следующего шага мы начнем рассмотривать использование DLL в макросах MS Office.