Delphi 3. Библиотека программиста

       

Создание текстового редактора


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

Создайте новый проект, поместите на форму компонент Memo и задайте его свойству Align значение alClient, чтобы он занял всю форму. Затем добавьте на форму компоненты MainMenu, OpenDialog и SaveDialog. В режиме конструирования меню добавьте три команды: Open, Save и Exit. Сохраните модуль формы в виде файла EDITFORM.PAS, а файл проекта — под именем TEXTEDIT.DPR. Готовая форма показана на рис. 2.1, а текст программы содержится в листинге 2.6.

Рис. 2.1. Готовая форма текстового редактора

Листинг 2.6. Форма текстового редактора, EDITFORM.PAS

{ EDITFORM.PAS — Простейший текстовый редактор, демонстрирующий использование DLL Автор: Джим Мишель Дата последней редакции: 12/05/97 } unit editform; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; OpenDialog1: TOpenDialog; SaveDialog1: TSaveDialog; MainMenu1: TMainMenu; File1: TMenuItem; Open1: TMenuItem; Save1: TMenuItem; N1: TMenuItem; Exit1: TMenuItem; procedure Exit1Click(Sender: TObject); procedure Open1Click(Sender: TObject); procedure Save1Click(Sender: TObject); private { Private declarations } FileName : String; procedure OpenFile(Filename: String); procedure SaveFile(Filename: String); public { Public declarations } end; var Form1: TForm1; implementation

{$R *.DFM} uses IniFiles; procedure TForm1.Exit1Click(Sender: TObject); begin Close; end; procedure TForm1.Open1Click(Sender: TObject); begin if OpenDialog1.Execute then OpenFile (OpenDialog1.FileName); end; procedure TForm1.Save1Click(Sender: TObject); begin if SaveDialog1.Execute then SaveFile (SaveDialog1.FileName); end; procedure TForm1.OpenFile (Filename: String); begin Memo1.Lines.LoadFromFile(Filename); end; procedure TForm1.SaveFile (Filename: String); begin Memo1.Lines.SaveToFile (Filename); end; end.

Протестируйте программу и убедитесь в том, что она загружает и сохраняет ASCII-файлы (годится любой файл с расширением .TXT, а также .PAS и .DPR).

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

Общая идея — поместить в INI-файл описание файлового формата, стандартное расширение и имя DLL, выполняющей преобразование. Пример такого INI-файла приведен в листинге 2.7.

Листинг 2.7. Файл TEXTEDIT.INI

; TEXTEDIT.INI ; Пример интерфейса расширения для файловых преобразований [Text] Extension=.TXT ConvertDLL=textconv.dll [Word for Windows] Extension=.DOC ConvertDLL=wfwconv.dll [WordCruncher] Extension=.WCX ConvertDLL=wcxconv.dll

Нам придется изменить процедуру OpenFile так, чтобы она просматривала расширение имени открываемого файла и затем вызывала функцию преобразования из соответствующей DLL. Функция читает файл, преобразовы вает текст и возвращает результат в виде списка строк. Для выполнения всех преобразований используется функция Convert, вызываемая из текстового редактора. В листинге 2.8 содержится новый вариант функции OpenFile (не забудьте добавить модуль IniFiles в строку uses модуля формы), а в листингах 2.9 и 2.10 — исходный текст DLL текстовых преобразований (TEXTCONV.DLL).

Листинг 2.8. Новая функция OpenFile

procedure TForm1.OpenFile (Filename: String); type ConvertFunc = function (Filename: String; Strings: TStrings): boolean; stdcall; var ConvertIni : TIniFile; ConvertList : TStringList; FileExt : String; Extension : String; DLLName : String; x : Integer; Found : Boolean; LibInstance : HMODULE; Converter : ConvertFunc; IniFileName : String; begin FileExt := UpperCase (ExtractFileExt (Filename)); IniFileName := ExtractFileDir (ParamStr (0)) + "\TEXTEDIT.INI"; ConvertIni := TIniFile.Create (IniFileName); ConvertList := TStringList.Create; { Считываем список возможных преобразований } ConvertIni.ReadSections (ConvertList); { Для каждого преобразования читаем значение Extension и сравниваем его с расширением выбранного файла. } x := 0; Found := False; while ((x < ConvertList.Count) and (Not Found)) do begin Extension := ConvertIni.ReadString ( ConvertList.Strings[x], "Extension", ""); if (UpperCase (Extension) = FileExt) then Found := True else x := x + 1; end; if Found then begin DLLName := ConvertIni.ReadString ( ConvertList.Strings[x], "ConvertDLL", ""); { Загружаем DLL, получаем адрес функции Convert и вызываем ее. } LibInstance := LoadLibrary (PChar(DLLName)); if LibInstance = 0 then begin Application.MessageBox ( PChar ("Can"'t load DLL "+DLLName), "TextEdit", MB_ICONEXCLAMATION or MB_OK); end else begin Converter := GetProcAddress (LibInstance, "Convert"); if Not Assigned (Converter) then begin Application.MessageBox ( PChar ("Can"'t find Convert function in "+DLLName), "TextEdit", MB_ICONEXCLAMATION or MB_OK); end else begin if not Converter (Filename, Memo1.Lines) then begin Application.MessageBox ( "Error loading file", "TextEdit", MB_ICONEXCLAMATION or MB_OK); end; end; FreeLibrary (LibInstance); end; end else begin Application.MessageBox ( PChar("No conversion supplied for file type "+FileExt), "TextEdit", MB_ICONEXCLAMATION or MB_OK); end; ConvertList.Free; ConvertIni.Free; end;

Листинг 2.9. Файл TEXTCONV.DPR

{ TEXTCONV.DPR — DLL текстовых преобразований Автор: Джим Мишель Дата последней редакции: 12/05/97 } library textconv;

{ Важное замечание об управлении памятью в DLL: модуль ShareMem должен стоять на первом месте в секции USES библиотеки, А ТАКЖЕ в секции USES вашего проекта (команда View|Project Source), если ваша DLL экспортирует какие-либо процедуры или функции, использующие строки в качестве параметров или результатов функций. Это относится ко всем строкам, передаваемым вашей DLL или получаемым от нее — даже если эти строки вложены в записи или классы. ShareMem представляет собой интерфейсный модуль для менеджера памяти DELPHIMM.DLL, который должен использоваться вместе с вашей DLL. Чтобы обойтись без использования DELPHIMM.DLL, передавайте строковую информацию в параметрах типа PChar или ShortString. } 1

uses ShareMem, SysUtils, Classes, textc in "textc.pas"; Exports textc.Convert index 1 name "Convert"; begin end.

1Этот комментарий создается средой Delphi автоматически. Поскольку далее в тексте идет его обсуждение, здесь приведен русский перевод. — Примеч. ред.

Листинг 2.10. Модуль TEXTC.PAS

{ TEXTC.PAS — Модуль текстовых преобразований. Загружает текстовые файлы с диска. Автор: Джим Мишель Дата последней редакции: 12/05/97 } unit textc; interface uses Classes; function Convert (Filename: String; Strings: TStrings) : boolean; stdcall; export; implementation function Convert (Filename: String; Strings: TStrings) : boolean; stdcall; begin Strings.LoadFromFile (Filename); Result := True; end; end.

Обратите внимание на примечание в начале листинга 2.9 (TEXTCONV.DPR). Оно автоматически вставляется в файл проекта при выполнении команды File|New DLL. Честно говоря, я не уверен в том, что в данном случае ссылка на модуль ShareMem так уж необходима. Я попытался запустить программу без ShareMem, и она нормально работала. Кроме того, могу выдвинуть следующий аргумент: я передаю функции Convert не сам класс, а лишь указатель на объект TStrings. Впрочем, примечание, скорее всего, относится и к указателям на классы, поэтому на всякий случай я включил ShareMem в секции uses программы и DLL. Если вам придется использовать ShareMem, не забудьте поставлять файл DELPHIMM.DLL вместе с приложением.

Функция OpenFile из листинга 2.8 ни в коем случае не годится для коммерческой программы. Это лишь пример, который иллюстрирует общую концепцию. В коммерческом варианте ваша программа должна читать файл и (по возможности) определять его тип, а затем запрашивать у пользователя разрешение на выполнение преобразования, прежде чем начинать что-либо делать. Данный пример лишь показывает, как можно реализовать интерфейс расширения для вашего продукта.



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