Читать книгу: «Иcпользование API на Delphi 7»
ЛЕКЦИЯ №1
1. РАБОТА С INI – ФАЙЛАМИ
Ini-файл – это текстовый файл, в котором информация сгруппирована по секциям, ключам. Данные файлы широко применяются во многих приложениях и в операционной системе Windows для хранения различных программных данных и настроек пользователя. В таком файле можно хранить, например, положение окон программы, дату, время и продолжительность работы программы, пути к разным данным и данным, выбранные в программе настройки пользователя Ниже приведена структура и пример Ini-файла:

Для работы с Ini-файлами применяется дополнительный модуль Delhi «inifiles». Ссылку на данный модуль необходимо прописать в разделе «uses» работающей с Ini-файлами форме программы. Вся работа с Ini-файлами осуществляется через тип «TiniFile» модуля «inifiles». Основные методы данного типа следующие:
| Create | Открытие Ini-файла для чтения или записи |
| Free | Закрытие открытого Ini-файла |
| UpdateFile | Обновляет данные из/в файла на диске |
| SectionExists | Проверка существования в файле данной секции |
| ValueExists | Проверка существования в файле ключа в данной секции |
| ReadSections | Получение перечня всех секций файла |
| EraseSection | Удаление данной секции со всеми ее ключами |
| DeleteKey | Удаление ключа в данной секции |
| ReadSection | Получение перечня ключей данной секции |
| ReadSectionValues | Получение значений всех ключей (с именами) данной секции |
| ReadString | Чтение строкового значения из ключа данной секции |
| ReadBool | Чтение логического значения из ключа данной секции |
| ReadDate | Чтение даты из ключа данной секции |
| ReadDateTime | Чтение даты и время из ключа данной секции |
| ReadFloat | Чтение вещественного значения из ключа данной секции |
| ReadInteger | Чтение целого значения из ключа данной секции |
| ReadTime | Чтение времени из ключа данной секции |
| WriteString | Запись (обновление) строкового значения в ключ данной секции |
| WriteBool | Запись логического значения в ключ данной секции |
| WriteDate | Запись даты в ключ данной секции |
| WriteDateTime | Запись даты и времени в ключ данной секции |
| WriteFloat | Запись вещественного значения в ключ данной секции |
| WriteInteger | Запись целого значения в ключ данной секции |
| WriteTime | Запись времени в ключ данной секции |
Рассмотрим на подробном примере процедуру записи информации в Ini-файл:
procedure TForm1.Button3Click(Sender: TObject);
var t_Ini: TIniFile;
k: integer; s, path_pr, nfile: string; td: tdatetime;
begin
k:=736; s:='Okey'; td:=now;
path_pr:=ExtractFilePath(application.exename) +'tsn.ini';
t_Ini := TIniFile.Create(nfile);
try
with t_Ini do begin
WriteInteger('TSN', 'str1', k);
WriteFloat('TSN', 'str2', 736.123);
WriteString('TSN', 'str3', s);
WriteDate('TSN', 'str4', strtodate('01.02.2012'));
WriteTime('TSN', 'str5', td);
Writebool('TSN', 'str6', true);
end;
except
showmessage('Нет доступа к Ini-файлу !');
end;
t_Ini.Updatefile; t_Ini.Free;
end;
Рассмотрим на примере процедуру считывания ранее сохраненной информации из Ini-файла:
procedure TForm1.Button4Click(Sender: TObject);
var t_Ini: TIniFile;
k: integer; s, path_pr, nfile: string; td: tdatetime;
begin
path_pr:=ExtractFilePath(application.exename) +'tsn.ini';
t_Ini := TIniFile.Create(nfile);
try
with t_Ini do begin
k:=ReadInteger('TSN', 'str1', -1); showmessage(inttostr(k));
s:=ReadString('TSN', 'str3', '-1'); showmessage(s);
td:=ReadDate('TSN', 'str4', strtodate('1.1.2000'));
showmessage(datetostr(td));
showmessage(floattostr(ReadFloat('TSN', 'str2', -1.123)));
showmessage(timetostr(ReadTime('TSN2', 'str5', now)));
showmessage(inttostr(byte(ReadBool('TSN2', 'str6', false))));
end;
except
showmessage('Нет доступа к Ini-файлу !');
end;
t_Ini.Free;
end;
ЛЕКЦИЯ №2
1. РАБОТА С РЕЕСТРОМ WINDOWS
Реестр Windows – это база данных, в которой операционная система Windows и внешние программы сохраняют нужные им данные. Логическая структура реестра напоминает строение Ini-файла, отличие состоит в том, что Ini-файл состоит из разделов (секций) и ключей раздела со значениями, а реестр состоит из разделов, вложенных подразделов (неограниченное число) и ключей (параметров) разделов/подразделов со значениями.
Реестр Windows имеет древовидную структуру, подобную файловой системе, где разделы и подразделы соответствуют каталогам и подкаталогам, а ключи со значениями – именам файлов:

Рис. 1
Любая программа может сохранять свои данные или в реестре Windows или в Ini-файлах. Достоинства и недостатки использования реестра и Ini-файлов следующие:
| Параметр | Использование реестра Windows | Использование Ini-файлов |
| Перенос данных с компьютера на компьютер | Трудность переноса данных с использованием экспорта/импорта ветвей реестра через программу редактора реестра («regedit») | Легкость переноса данных путем простого копирования Ini-файла с одного компьютера на другой |
| Модификация данных | Через функции прикладной программы или программу редактора реестра | Через функции прикладной программы или корректировки в обычном текстовом редакторе |
| Привязка к конкретному пользователю Windows | Автоматически на уровне операционной системы через корневой раздел «HKEY_CURRENT_USER» | Трудно через функции прикладной программы |
| Привязка к конкретному компьютеру | Автоматически на уровне операционной системы через корневой раздел «HKEY_LOCAL_MACHINE» | Трудно через функции прикладной программы |
Таблица 1
Реестр состоит из шести корневых разделов, два из которых представляют наибольший интерес: раздел «HKEY_LOCAL_MACHINE» предназначен для хранения данных, доступных всем пользователям Windows, а раздел «HKEY_CURRENT_USER» хранит данные, доступные только для текущего пользователя Windows. Другие корневые разделы обычно используются только операционной системой и некоторыми системными программами.
Язык программирования Delphi 5 позволяет производить с реестром Windows любые манипуляции через подключение модуля «Registry» в разделе «interface -> uses». Рассмотрим на примере основные базовые операции при записи и считывании данных с реестра:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
{ Процедура записи данных в реестр }
var Reg: tRegistry;
begin
Reg:=tRegistry.Create;
Reg.RootKey:=HKEY_LOCAL_MACHINE;
try
if Reg.OpenKey('\Software\TSN736\', true) then begin
if not Reg.KeyExists('setup') then Reg.CreateKey('setup');
if Reg.OpenKey('\Software\TSN736\setup', false) then begin
Reg.WriteInteger('Left', form1.left); Reg.WriteInteger('Top', form1.top);
Reg.CloseKey;
end;
end;
finally
Reg.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
{ Процедура чтения данных с реестра }
var Reg: tRegistry;
begin
Reg:=tRegistry.Create;
Reg.RootKey:=HKEY_LOCAL_MACHINE;
try
if Reg.OpenKey('\Software\TSN736\setup', false) then begin
try form1.left:=Reg.ReadInteger('Left'); except; end;
try form1.top:=Reg.ReadInteger('Top'); except; end;
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
ЛЕКЦИЯ №3
1. ПРЕОБРАЗОВАНИЕ ТИПОВ. ДОСТУП К ОБЪЕКТАМ И КОМПОНЕНТАМ ЧЕРЕЗ УКАЗАТЕЛИ
При написании обработчиков событий для компонент практически всегда в процедуру передается указатель «Sender» типа «TObject». Данный указатель как правило указывает (ссылается) на тот визуальный компонент, который вызывает данную процедуру. Параметр «Sender» является некоторым усовершенствованным аналогом ссылочной переменной языка Pascal.
Использование параметра «Sender» позволяет делать процедуры – обработчики событий универсальными, позволяющими работать не с одним конкретным компонентом, а с несколькими, имеющими одинаковый тип. Например, можно написать процедуру перемещения кнопки по экрану и привязать эту процедуру ко всем кнопкам формы, после чего все кнопки также смогут перемещаться по экрану. Для этого в процедуре-обработчике необходимо пользоваться только «Sender» конструкциями, а не явными указаниями на компоненты.
Рассмотрим способы доступа к компонентам через указатель «Sender» и методы преобразования типов:
procedure TForm1.Button2Click(Sender: TObject);
var s: TObject; ss: string;
{ Пример опознания типа объекта по указателю "Sender" }
begin
s:=Sender; { или так: s:=button2; }
ss:='tbutton'; { запишем имя типа кнопки в строк. переменную }
if s.ClassNameIs(ss) then showmessage('Кнопка!');
if (s is tbutton) then showmessage('Это кнопка типа "TButton" !')
else showmessage('Это "'+s.ClassName+'" !');
{ Можно так }
try (s as tbutton).caption:='Ok!!!'; except end;
{ Или так }
tbutton(s).caption:='Ok!!!';
{ Или вот-так, если свойство не "Protected" и не "Read-only" }
tcontrol(s).left:=300;
end;
ЛЕКЦИЯ №4
1. ТЕХНОЛОГИЯ ПЕРЕТАСКИВАНИЯ ОБЪЕКТОВ «DRAG & DROP»
Технология «Drag & Drop» («Перетащи и кинь») предназначается для перемещения (перетаскивания) данных из одних визуальных компонент в другие в процессе работы программы с помощью мыши. Данная технология делает интерфейс программы более удобным и быстрым и используется во многих системных и прикладных Windows-программах.
Технология «Drag & Drop» позволяет, например, копировать строку текста (пункт) из одного компонента формы «ListBox» в другой компонент «ListBox» или предложение из одного компонента «Memo» в другой компонент «Memo». Технология «Drag & Drop» позволяет перемещать данные из любого визуального компонента в любой другой визуальный компонент для любой формы текущего приложения. С окнами других приложений данная технология по умолчанию не работает.
Для перетаскивания данных из одних визуальных компонент в другие с помощью технологии «Drag & Drop» необходимо на этапе создания программы настроить компоненты на данную технологию и запрограммировать алгоритм их поведение при перетаскивании из (в) них данных.
Для того, чтобы компонент смог «откликаться» (выделять перетаскиваемый компонент другим цветом и менять форму курсора) на начало процесса перетаскивания из него данного, необходимо его свойству «DragMode» присвоить значение «dmAutomatic». После этого компонент сможет предоставлять выделенное данное в распоряжение технологии «Drag & Drop» для перетаскивания.
Edit1.DragMode:=dmAutomatic; ListBox1.DragMode:=dmAutomatic;
Компонент, давший данное для перетаскивания, отдает управление Widows и выходит из участия из процесса перетаскивания. Далее Windows следит за тем, куда пользователь перемещает данное по компонентам формы. Как только данное будет находится над каким-нибудь визуальным компонентом, Windows вызовет у него событие «OnDragOver» и передаст в соответствующую ему процедуру – обработчик данное о том, от кого (какого компонента) прибыло перетащенное данное. Компонент, над которым находится перетаскиваемое данное, в процедуре-обработчике события «OnDragOver» анализирует его и если ему оно нужно, то устанавливает параметру процедуры «Accept» значение «true», или значение «false» в противном случае. Значение «Accept» равное «true» озночает, что компонент не возражает принять «зависшее» над ним данное.
procedure TForm1.Edit1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
{ Можно принять (едиту), если "кидается" на него метка или данные из лист-бокса}
begin Accept := (Source is TEdit) or (Source is TListBox); end;
Если данное переместится на другой компонент, или пользователь отменит перетаскивание клавишей клавиатуры «Esc», то компонент «забывает» о «зависшем» над ним данным и нечего больше не предпринимает. Если же пользователь отпустил данное над компонентом, то у компонента возникает событие «OnDragDrop».
В обработчик события «OnDragDrop» передается данное о том, кто послал данное и в какое место компонента его кинули. Именно данная процедура обработчик (у компонента-приемника данного) реализует алгоритм перемещения данного с другого компонента в данный. Операционная система Windows в данной технологии служит только посредником для визуального отображения процесса перетаскивания, и не участвует в фактической обработке процесса переноса данных.
В процедуре – обработчике события «OnDragDrop» необходимо программно реализовать алгоритм перемещения информации из одного визуального компонента, в другой.
procedure TForm1.Edit1DragDrop(Sender, Source: TObject; X, Y: Integer);
{ Обработка факта "кидка" для едита; Sender – на кого кинули; Source – что кинули (кого) }
begin { Если кинули из лист-бокса, то …}
if Source is TListbox then with Sender as TEdit do begin
Text:=(Source as TListbox).items[(Source as TListbox).itemindex];
{ если кинули из другого едита , то … }
end else (Sender as TEdit).Text:=(Source as TEdit).text;
end;
ЛЕКЦИЯ №5
1. СОЗДАНИЕ КЛИЕНТ – СЕРВЕРНЫХ СИСТЕМ
С ИСПОЛЬЗОВАНИЕМ СОКЕТНОЙ ТЕХНОЛОГИИ ПРОТОКОЛА TCP/IP
В настоящее время наиболее распространенным протоколом передачи данных в локальной сети и Интернет является протокол TCP/IP. Данный протокол позволяет передавать любые данные на любое расстояние. Протокол – это специальное программное обеспечение, входящее в операционную систему, которое реализует прием и передачу данных по сети.
При связи двух программ (компьютеров) между собой по сети через протокол TCP/IP для каждой программы операционная система создает (открывает) свой «сокет». Сокет – это буфер (канал) обмена данными по сети. Сокет открывается для конкретного экземпляра программы для указанного сетевого адреса (идентифицирующий компьютер в сети) и порта TCP/IP (идентифицирующий конкретную сетевую программу). Каждый сокет получает от операционной системы уникальный номер (дескриптор, хэндл).
При обмене данными по протоколу TCP/IP одна машина обычно выступает в роли "сервера", другая – в роли "клиента". Клиент, как и сервер, может передавать и принимать данные, отличие между ними заключается в том, что программа "сервер" может одновременно работать с несколькими "клиентами", а "клиент" – только с одним сервером. На рис. 1 показана схема модели клиент – серверного взаимодействия на основе сокетов для протокола TCP/IP.
Для создания сокетного клиента TCP/IP в Делфи используется компонент «ClientSocket», а для создания сокетного сервера компонент «ServerSocket». Используя свойства, методы и события данных компонент создадим программы «клиент» и «сервер».
2. СОЗДАНИЕ ПРОГРАММЫ TCP/IP – КЛИЕНТ НА ОСНОВЕ СОКЕТОВ
Компоненты программы – клиента:
ClientSocket1: TClientSocket – компонент сокета клиента;
Edit1 – IP-адрес сервера;
Edit2 -порт TCP/IP;
Memo1 – посылаемые на сервер данные;
ListBox1 – получаемые с сервера данные;
Button1 – активизация клиента (подключение);
Button2 – посылка данных;
Button3 – отключение клиента.
Свойства ClientSocket1:
ClientType – тип передачи данных по протоколу TCP/IP (блочная передача, не блочная);
Active – открытие/закрытие связи с сервером (сокета);
Address – IP адрес сервера TCP/IP;
Host – имя сервера TCP/IP (задается либо адрес, либо хост сервера);
Port – номер порта TCP/IP сервера;
Socket – объект доступа к сокету.
События ClientSocket1:
OnConnect – подключение к серверу;
OnDisconnect – отключение от сервера;
OnError – ошибка при подключении к серверу;
OnRead – чтение данных с сервера.
В процедуры-обработчики данных событий
передается параметр «Socket» типа
“TCustomWinSocket”. Данный параметр имеет
свойство «Socket . ReceiveText» – хранящий принятые с сервера данные.
Методы ClientSocket1:
Socket . SendText – отправка текста на сервер;
Open – подключение к серверу (открытие сокета);
Close – отключение от сервера (закрытие сокета).

procedure TForm1.Button1Click(Sender: TObject);
{ Подключение к серверу }
begin
{ Если соединение уже установлено – прерываем его }
ClientSocket1.Close;
{ Устанавливаем не блочную передачу/прием данных }
ClientSocket1.ClientType:=ctNonBlocking;
{ Присваиваем свойствам Address и Port нужные значения }
ClientSocket1.Address := Edit1.Text; ClientSocket1.Port := StrToInt(Edit2.Text);
{ Пытаемся открыть сокет и установить соединение с сервером }
ClientSocket1.Open;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
{ Отключение от сервера }
begin
ClientSocket1.Close;
end;
procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
{ Прием текста от сервера }
begin
ListBox1.Items.Text:=Socket.ReceiveText;
end;
procedure TForm1.Button2Click(Sender: TObject);
{ Отправка текста на сервер }
begin
if ClientSocket1.Socket.SendText(Memo1.lines.Text)=0 then
MessageDlg('Не могу отправить данные на сервер',
mtError,[mbOk], 0) else showmessage('Ok!');
end;
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
{ Произошла ошибка соединения с сервером }
begin
ErrorCode:=0; MessageDlg('Нет связи с сервером', mtError,[mbOk], 0);
end;
procedure TForm1.Button3Click(Sender: TObject);
{ Отключение от сервера }
begin
ClientSocket1.Close;
end;
procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
{ Факт подключения к серверу }
begin
ListBox1.Items.Text:='On line…';
end;
procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
{ Факт отключения к серверу }
begin
ListBox1.Items.Text:='Off line…';
end;
3. СОЗДАНИЕ ПРОГРАММЫ TCP/IP – СЕРВЕР НА ОСНОВЕ СОКЕТОВ
Компоненты программы – сервера:
ServerSocket1: TServerSocket – компонент – сокет сервера TCP/IP;
Edit1 – IP-адрес сервера;
Memo1 – посылаемые на клиент данные;
ListBox1 – полученные от клиента данные;
Button1 – запуск сервера;
Button2 – остановка сервера;
Button3 – отправка данных на клиенты.
Свойства ServerSocket1:
Active – запуск/остановка сервера (сокета);
Port – номер порта TCP/IP сервера;
Socket – объект доступа к сокету;
Socket . ActiveConnections – количество подключенных к серверу клиентов.
События ServerSocket1:
OnClientConnect – подключение клиента к серверу;
OnClientDisconnect – отключение клиента от сервера;
OnClientError – ошибка связи с клиентом;
OnClientRead – получение данных от клиента;
OnListen – ожидание подключения клиента.
В процедуры-обработчики данных событий передается параметр
«Socket» типа “TCustomWinSocket”.
Данный параметр имеет свойство
«Socket . ReceiveText» – хранящий принятые с клиента данные.
Методы ServerSocket1:
Socket . Connections[i] . SendText – отправка на i-й клиент текста;
Open – активизация сервера (открытие сервера);
Close – деактивизация сервера (закрытие сервера).
(!) Каждый сокет имеет свойство «Socket . SocketHandle» – уникальный дескриптор данного сокета, используемый для идентификации клиента (в обработчике события «OnClientRead».
procedure TForm2.Button1Click(Sender: TObject);
{ Определяем порт и запускаем сервер }
begin
ServerSocket1.Close; ServerSocket1.Port := StrToInt(Edit1.Text); ServerSocket1.Open;
end;
procedure TForm2.Button2Click(Sender: TObject);
{ Останавливаем сервер }
begin
ServerSocket1.Close; ListBox1.Items.Text:='Server is stop.';
end;
procedure TForm2.ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
{ Произошла ошибка связи с клиентом }
begin
MessageDlg('Нет связи с клиентом', mtError,[mbOk], 0);
end;
procedure TForm2.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
{ Получение данного с клиента }
begin
Listbox1.Items.Text:=Socket.ReceiveText;
end;
procedure TForm2.Button3Click(Sender: TObject);
{ Отправка данных на клиент }
label 1;
begin
with ServerSocket1.Socket do begin
if ActiveConnections=0 then goto 1;
if Connections[0].SendText(Memo1.Lines.text)=0 then
1: MessageDlg('Не могу отправить данные на клиент',
mtError,[mbOk], 0) else showmessage('Ok!');
end;
end;
procedure TForm2.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
{ Запрет доступа "вторым" клиентам }
begin
if ServerSocket1.Socket.ActiveConnections>1 then abort;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
{ Остановка сервера }
begin
ServerSocket1.Close;
end;
procedure TForm2.ServerSocket1Listen(Sender: TObject;
Socket: TCustomWinSocket);
{ Ожидание подключения клиентов }
begin
ListBox1.Items.Text:='Server is active…';
end;
ЛЕКЦИЯ №6
1. РАЗРАБОТКА DLL – БИБЛИОТЕК
Язык программирования Delphi является языком визуального объектно-ориентированного программирования (ООП). В современных программах, совместно с ООП, очень часто используется и модульный принцип создания программ. Наибольшее развитие данная технология получила в форме разработки динамически загружаемых DLL-библиотек. DLL-библиотека представляет собой отдельный файл с расширением «DLL», в котором находятся какие-либо процедуры и функции. Они становятся доступными при подключении к программе DLL-файла.
При подключении DLL-библиотеки к программе она загружается в оперативную память компьютера и находится там до завершения программы. Если запускается несколько программ, и всем им нужна одна и таже DLL-библиотека, то она загрузится в память в одном экземпляре, а выгрузится из памяти при закрытии последней (использующей библиотеку) программы.
При одновременной работе нескольких программ с одной и той же DLL-библиотекой обеспечивается независимость переменных DLL-модуля для каждой прикладной программы. Таким образом получается, что процедуры и функции загружаются в одном экземпляре, а переменные DLL копируются для каждой прикладной программы отдельно.
Прикладная программа на Delphi может подключать DLL-библиотеку двумя способами: динамическим и статическим. При статическом способе DLL-библиотека автоматически подключается при запуске программы и автоматически деактивируется при ее завершении. Если указанный DLL-файл будет не найден, то возникнет ошибка выполнения программы. При динамическом способе DLL-библиотека подключается и отключается в нужное программисту время, при этом обеспечивается контроль над ошибочными ситуациями (например, если DLL-файл не найден).
Для создания файла DLL в Delphi необходимо выбрать пункт меню: «File -> New Item -> DLL wizard». Исходный код DLL-библиотеки нужно набрать в открывшимся окне и сохранить как файл с расширением «DPR». Никакие формы и файлы с расширение «PAS» здесь не используются.
Для одновременной отладки и создания программы и DLL-библиотеки лучше всего запустить две копии «Delphi» и в них работать отдельно.
1.1. Пример простейшей библиотеки DLL
library demo_dll;
function sc(x: real): real export; stdcall; { Функция расчета }
begin
xsin:=sin(x)+ cos(x);
end;
exports sc; { Экспорт процедур и функций для внешних программ }
begin { Секция инициализации библиотеки DLL }
end.
end.
1.2. Пример использования библиотеки DLL
unit Demo_dll;
…
var Form1: TForm1;
function sc(x: real): real; stdcall; external 'demo_dll.dll'; { Подключение функции из DLL-библиотеки }
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(floattostr(sc(5)));
end;
1.3. Пример расширенной библиотеки DLL
library repa;
uses SysUtils, Classes, Windows, Registry, StdCtrls;
procedure Save1(edit1_text: pchar; mm1: pchar; scrollbar1_position: integer); export; stdcall;
// Сохранение данных в реестр
var nfile, s: string; t: tregistry; i,k: integer; m1: tstrings;
begin
m1:=TStringList.Create; m1.Text:=mm1;
t:=tRegistry.Create;
t.RootKey:=HKEY_CURRENT_USER;
t.OpenKey('\Software\is-302\tsn-12', true);
t.writestring('', 'repa.dll');
t.WriteString('Edit1_text', edit1_text);
k:=m1.count;
t.WriteInteger('memo1_count', k);
for i:=0 to m1.Count-1 do begin
s:=m1[i]; t.Writestring('memo1_L'+inttostr(i+1), s);
end;
t.WriteInteger('scrollbar1_position', scrollbar1_position);
t.closekey; m1.Free;
t.Free;
end;
procedure Load1(var edit1_text: pchar; var mm1: pchar; var scrollbar1_position: integer); export; stdcall;
// Считывание данных из реестра
var path_pr, nfile, s,m: string; t: tregistry; i,k,l: integer; m1,edit: tstrings;
begin
m1:=TStringList.Create;
t:=tRegistry.Create;
t.RootKey:=HKEY_CURRENT_USER;
t.OpenKey('\Software\is-302\tsn-12', true);
m1.text:='';
try
edit1_text:=pchar(t.Readstring('edit1_text'));
except
edit1_text:='????';
end;
try
k:=t.readInteger('memo1_count');
for i:=0 to k-1 do begin
s:= t.readstring('memo1_L'+inttostr(i+1));
m1.Add(s);
end;
except
end;
mm1:=pchar(m1.text);
try
l:= t.readInteger('scrollbar1_position'); scrollbar1_Position:=l;
except
end;
t.closekey; m1.Free; t.Free;
end;
procedure Clear1(var edit1_text: pchar; var mm1: pchar; var scrollbar1_position: integer); export; stdcall;
// Очистка данных
begin
edit1_text:=''; mm1:=''; scrollbar1_position:=0;
end;
exports Clear1, Save1, Load1;
begin
end.
1.4. Пример использования расширенной библиотеки DLL
unit Unit1;
…
var
Form1: TForm1;
procedure Save1(edit1_text: pchar; mm1: pchar; scrollbar1_position: integer); stdcall; external 'repa.dll';
procedure Load1(var edit1_text: pchar; var mm1: pchar; var scrollbar1_position: integer); stdcall; external 'repa.dll';
procedure Clear1(var edit1_text: pchar; var mm1: pchar; var scrollbar1_position: integer); stdcall; external 'repa.dll';
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
// Запись данных
begin
Save1(Pchar(edit1.text), Pchar(memo1.Lines.Text), scrollbar1.Position);
end;
procedure TForm1.Button2Click(Sender: TObject);
// Очистка данных
var e,m: pchar; k: integer;
begin
clear1(e, m, k);
edit1.text:=e; memo1.Lines.Text:=m; scrollbar1.Position:=k;
end;
procedure TForm1.Button3Click(Sender: TObject);
// Загрузка данных
var e,m: pchar; k: integer;
begin
Load1(e, m, k);
edit1.text:=e; memo1.Lines.Text:=m; scrollbar1.Position:=k;
end;
end.
ЛЕКЦИЯ №7
1. API – ФУНКЦИИ WINDOWS УПРАВЛЕНИЯ ВНЕШНИМИ ПРИЛОЖЕНИЯМИ
API-функции – это набор стандартных функций операционной системы Windows, через которые можно программно управлять всеми запущенными программами и ресурсами компьютера. API-функции являются некоторым современным аналогом функциям-прерываниям INT21h MS-DOS. Возможность использовать данные функции в Делфи организуется через автоматически подключаемый модуль «Windows».
Для получения справки по API-функциям из Делфи необходимо выбрать пункт «Help > Windows SDK (Win32 Developer's References)». Данный хелп содержит подробную информацию о правилах вызова основных API- функций (синтаксис языка «С»).
Все визуальные компоненты Делфи, например «Edit, Label, Button» и др. являются окнами, как и форма. Для универсальной работы с любыми окнами используются дескрипторы (handle) окон. Дескриптор окна – это указатель на окно в виде целого числа (аналог порядкового номера), типа «HWND» (прототип «Integer»). При открытии любого окна (видимого или невидимого, типа формы или кнопки), Windows присваивает ему уникальный дескриптор. Данный указатель действителен до закрытия окна или перезагрузки Windows. Любому окну Windows может присвоить любой дескриптор.
Для того, чтобы узнать дескрипторы окон какой-либо программы, применяется программа «WinSight32» из пакета программ Delphi или другая подобная программа.
| ПРИМЕР ВЫЗОВА API-ФУНКЦИИ | ПОЯСНЕНИЕ (VAR H,H2:HWND; I: LONGWORD; S: STRING;) |
| GetWindowText(h, pchar(s),I); | Получение текста из окна «h» в переменную «s» длиной не более «i» символов |
| i:=GetWindowtextLength(h); | Получение длины текста из окна «h» в переменную «i» |
| SetWindowText(h, pchar(s)); | Установка текста «s» в окне «h» |
| h:=FindWindow(pchar(s),nil); | Поиск дескриптора окна с именем класса «s» в переменную «h» |
| h2:=GetWindow(h, GW_CHILD); | Поиск подчиненного окна «h2» для окна «h» |
| h2:=GetWindow(h, GW_HWNDNEXT); | Поиск следующего подобного окна «h2» для окна «h» |
| If h=0 then WinExec(‘calc.exe’, SW_SHOWMODAL); | Если искомое окно не найдено, то запустить указанную программу «calc.exe» |
| ShowWindow(h, SW_HIDE); | Установить для окна «h» заданный режим отображения |
| ShowWindow(h, SW_MINIMIZE); | Установить для окна «h» заданный режим отображения |
| ShowWindow(h, SHOWNORMAL); | Установить для окна «h» заданный режим отображения |
| SetForegroundWindow(h); | Поместить видимое на экране окно «h» поверх всех окон |
| SendMessage(h, WM_LBUTTONDOWN,0,0); | Посылка окну «h» сообщение с параметрами (эмуляция нажатия левой кнопки мыши) |
| MoveWindow(h, 210,38,30,20,true); | Задать окну «h» координаты x и y верхнего левого угла, ширину, высоту и флаг автоматической перерисовки окна |
| CloseWindow(h); | Сворачивание окна «h» |
| PostMessage(h, WM_QUIT,0,0); | Посылка сообщения для закрытия окна «h» |
| Edit1.Perform(WM_CHAR, byte(“!”),0); | Посылка компоненту формы сообщения (эмуляция ввода символа в компонент, эмуляция нажатия стрелки вниз). Используется в собственных программах на Delphi |
| Combobox1.Perform( | Посылка компоненту формы сообщения (эмуляция ввода символа в компонент, эмуляция нажатия стрелки вниз). Используется в собственных программах на Delphi |
| WM_KEYDOWN, VK_DOWN,0); | |
| ShellExecute( handle, ’open’, ’c:\’, nil, nil, sw_shownormal) | Открывает нужную папку в Проводнике |
| ShellExecute( | Открывает документ «test.doc». Папка для временных файлов «c:\temp» |
| application.MainForm.handle, ’open’, ’test.doc’, | |
| nil, ’c:\temp’, sw_shownormal) | |
| ShellExecute( handle, ’open’, ’mailto: tsn_prof@mail.ru’, nil, nil, sw_shownormal) | Открывает почтовый клиент для создания письма для заданного электронного адреса |
| ShellExecute(handle, ’open’, ’http://www.pau.kz’, nil, nil, sw_shownormal) | Открывает Интернет-браузер для просмотра указанной WWW-страницы |
2. ПРИМЕР РАБОТЫ С API-ФУНКЦИЯМИ
function GetText1(Wnd: HWND): string;
{ Получение текста из окна, способ 1 (универсальный) }
var textlength: Integer; Text: PChar;
begin
textlength := SendMessage(Wnd, WM_GETTEXTLENGTH, 0, 0);
if textlength = 0 then Result := '' else begin
GetMem(Text, textlength + 1);
SendMessage(Wnd, WM_GETTEXT, textlength + 1, Integer(Text));
Result := Text;
FreeMem(Text);
end;
end;
function GetText2(h: HWND): string;
{ Получение текста из окна, способ 2 }
var i:LongWord; s:string;
begin
i:=GetWindowTextLength(h)+1; SetLength(s,i);
GetWindowText(h,pchar(s),i); SetLength(s,i-1);
GetText2:=s;
end;
procedure SetTxt1 (h: HWND; s: string);
{ Установка текста в окне (универсальный) }
begin
SendMessage(h,WM_SETTEXT,0, longint(pchar(s)));
end;
procedure SetTxt2 (h: HWND; s: string);
{ Установка текста в окне }
begin
SetWindowText(h,pansichar(s));
end;
Procedure ClickWnd(h: HWND);
{ "Нажатие" на окно левой кнопкой мыши }
begin
SendMessage(h,WM_LBUTTONDOWN,0,0);
SendMessage(h,WM_LBUTTONUP,0,0);
end;
procedure ClickTB(h: HWND; BTN_ID: integer);
{ "Нажатие" на кнопку тулбара }
begin
SendMessage(h, WM_COMMAND, BTN_ID, 0);
end;
procedure Button3Click(Sender: TObject);
{ Клик на кнопку "Добавить" в архиваторе "7z" }
var h: hwnd;
begin
h:=Findwindow('FM',nil);
if h=0 then winexec('C:\Program Files\7-Zip\7zFM.exe',sw_show);
h:=Findwindow('FM',nil); sleep(1000);
if h=0 then exit;
SetForeGroundWindow(h);
SetTxt2 (h,’ok’);
MoveWindow(h,210,38,30,20,true);
h:=GetWindow(h,gw_child);
h:=GetWindow(h,gw_child);
h:=GetWindow(h,GW_HWNDNEXT);
ClickTB(h, 2000);
end;
3. СОЗДАНИЕ ПРОГРАММНЫХ ЛОВУШЕК В DELPHI
unit Loader_DLL_TSN;
…
var
Form1: TForm1;
procedure hook(switch : Boolean); stdcall; external 'hook_dll_tsn.dll';
const IDM_TSN=$C0;
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
var h: HWND; p, pp: HMENU;
begin
h:=FindWindow('SciCalc',nil); if h=0 then winexec('calc',SW_SHOW);
h:=FindWindow('SciCalc',nil); if h=0 then exit else SetForegroundWindow (h);
hook(true); // Устанавливаем наш перехватчик событий Windows
p:=GetMenu(h); // Верхнее меню
// Меняем название пункта в верхнем меню "Справка"
ModifyMenu(p, 2, MF_BYPOSITION, GetMenuItemID(p,2), '-TSN-' );
pp:= GetSubMenu(p,2); // Подпункты в верхнем меню (с нуля отсчет)
// Меняем название подпункта в подменю "Справка -> О программе"
ModifyMenu(pp, 2, MF_BYPOSITION, GetMenuItemID(pp,2) , 'About Sergius …' );
// Добавляем новый элемент подменю с идентификатором IDM_TSN
AppendMenu(pp, MF_STRING ,IDM_TSN, 'А это новый пункт !');
DrawMenuBar(h); // Прорисовываем модифицированное меню
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
{ при закрытии формы – освобождаем Hook }
var h: HWND;
begin
hook(false); // Деактивизируем наш перехватчик событий Windows
h:=FindWindow('SciCalc',nil);
h:=FindWindow('SciCalc',nil); if h<>0 then Postmessage(h,WM_QUIT,0,0);
end;
end.
library HOOK_DLL_TSN;
uses Windows, Messages, SysUtils;
var SysHook: HHook = 0; Wnd: Hwnd = 0;
const IDM_TSN=$C0;
function SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;forward;
{Процедура установки HOOK-а}
procedure hook(switch : Boolean) export; stdcall;
begin
if switch=true then
// Устанавливаем хук на сообщения после их обработки в приложении
// Наша процедура захвата называется "SysHook", в данном модуле
SysHook := SetWindowsHookEx(WH_GETMESSAGE, @SysMsgProc, HInstance, 0)
else UnhookWindowsHookEx(SysHook);
end;
function SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
{ Обработчик перехвата }
label 1;
var dc: HDC; h, h2: HWND; x: cardinal; ss: string;
begin
{ Если нам не стоит обрабатывать это событие, то и не надо,
передадим упраление следующиму хуку }
if (code<0) or (code<>HC_ACTION) then goto 1;
// Ищем калькулятор, если он не запущен, но отдыхаем …
h:=FindWindow('SciCalc',nil); if h=0 then goto 1;
h2:=GetWindow(h,GW_CHILD); // Нашли окно отображения (табло) у кальулятора
// Если калькулятор не виден, то ничего не надо …
if not IsWindowVisible(h) then goto 1;
// Получаем указатель на текущее обрабатываемое окно, ждем калькулятор
Wnd:=TMsg(Pointer(lParam)^).hwnd;
// Механизм нашей прорисовки на калькуляторе для любого окна
ss:='Кликни правой по калку …';
dc:=GetDC(h); SetBkMode(dc, TRANSPARENT);
TextOut(dc,15,4,pansichar(ss),length(ss));
// –
x:=TMsg(Pointer(lParam)^).message; // Получаем код сообщения окна
// Запрещаем щелкать по окошку калькулятора
if (wnd=h2) and (x=WM_LBUTTONDOWN) then begin
MessageBox(0, 'Нельзя !!!', 'Щелкать по окошку',0 );
TMsg(Pointer(lParam)^).message:=0; // Анулируем сообщение
end;
// Механиз обработки "нашего" пункта меню в калькуляторе
if (wnd=h) and (x=WM_COMMAND)
and (lo(TMsg(Pointer(lParam)^).wParam) = IDM_TSN)
then MessageBox(0, 'Да!', 'Калькулятор говорит', 0);
{ Передаем сообщение дальше по цепочке hook-ов }
1: Result:=CallNextHookEx(SysHook, Code, wParam, lParam);
end;
exports hook;
begin
end.
ЛЕКЦИЯ №8
1. РАБОТА С ПРИЛОЖЕНИЕМ «MS WORD»
unit main;
uses … ComCtrls, WordXP, OleServer, Variants;
type TForm1 = class(TForm)
…
wa: TWordApplication;
wd: TWordDocument;
var Form1: TForm1; t, tt, vv: OleVariant; // параметры для передачи
implementation
…
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
wa := wa.Create(nil); // создаем экземпляр Word
wa.Visible := true; // делаем его видимым
t := 'Normal'; // шаблон
tt := false; // не создавть шаблон
wa.Documents.Add(t, tt, EmptyParam, EmptyParam); // добавляем документ
// подключаем к объекту WordDocument созданный документ
wd.ConnectKind := ckAttachToInterface; wd.ConnectTo(wa.ActiveDocument);
t := wd.Range; // переменная – параметр, шаблон для нового абзаца
// добавляем в документ 3 абзаца:
wd.Paragraphs.Add(t); wd.Paragraphs.Add(t); wd.Paragraphs.Add(t); // абзац #3
wd.Paragraphs.Item(1).Range.InsertBefore('Привет, TSN !!!'); // вставляем текст в абзац #1
// вставляем текст в абзац #2
wd.Paragraphs.Item(2).Range.InsertBefore('Переключившись в программу, можно программно менять текст ячеек !');
wd.Paragraphs.Item(1).Range.Font.ColorIndex := 11; // цвет зеленый в абзаце #1
wd.Paragraphs.Item(2).Range.Font.ColorIndex := 5; // цвет розовый в абзаце #2
wd.Paragraphs.Item(3).Range.Font.Bold := 1; // шрифт – жирный
wd.Paragraphs.Item(3).Range.Font.Italic := 1; // шрифт – рукописный
// добавляем таблицу
wd.Tables.Add(wd.Paragraphs.Item(3).Range, 5, 4, EmptyParam, EmptyParam); // 5-строк, 4- cтолбца
wd.Tables.Item(1).Range.Font.ColorIndex := 2; // цвет синий в таблице
// в цикле задаем значение ячеек, добавляем текст
for i := 1 to 5 do wd.Tables.Item(1).Cell(i, 1).Range.Text := IntToStr(i);
end;
procedure TForm1.FormDestroy(Sender: TObject);
var s: OleVariant; // для параметров
begin
try
s:=False; //изменения не сохранять
wd.Close(s,EmptyParam,EmptyParam); // закрыть сначала документ
wa.Quit(s,EmptyParam,EmptyParam); // а потом и ворд
except end; // ничего не предпринимаем, если ворд уже закрыт
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
// в соотв ячейку ставим соотв значение, а можно и наоборот
wd.Tables.Item(1).Cell(UpDown2.Position,UpDown1.Position).Range.Text:=Edit3.Text;
end;
end.
2. РАБОТА С ПРИЛОЖЕНИЕМ «MS EXCEL»
unit Main;
….
private
XLApp: Variant;
public
implementation
uses … ComObj, Variants;
const cXL1 = -4167; cXL2 = 1;
procedure TForm1.FormDestroy(Sender: TObject);
{ Уничтожение формы }
begin
{ Если открыт нужный лист Excel, то … }
if not VarIsEmpty(XLApp) then begin
XLApp.DisplayAlerts := False; // если True – то спрашивает о сохр.
XLApp.Quit; // Закрытие Excel'a
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var R, C, S: Variant; i: Integer;
begin
XLApp:= CreateOleObject('Excel.Application'); // Подключение к Excel
XLApp.Visible := True; // Включение Excel на экран
XLApp.Workbooks.Add(cXL1); // Добавляем новый лист
// Задаем листу название
XLApp.Workbooks[1].WorkSheets[1].Name := 'Delphi Data';
// Вставка данных в лист
S := XLApp.Workbooks[1].WorkSheets['Delphi Data'];
for i := 1 to 10 do S.Cells[i, 1] := i;
S.Cells[i, 1] := '=Sum(A1:A10)'; // Вставка формулы
// Работа с областью (рангом)
R:=XLApp.Workbooks[1].WorkSheets['Delphi Data'].Range['C1:F25'];
R.Formula := '=RAND()'; // Вставка формулы в ранг
R.Columns.Interior.ColorIndex := 3; // Цвет закраски фона
R.Font.Color := clGreen; // Цвет ранга
R.Borders.LineStyle := cXL2; // Ячейки обрамленные
// Работа с колонкой и шрифтом
C:=XLApp.Workbooks[1].WorkSheets['Delphi Data'].Columns;
C.Columns[1].ColumnWidth := 5; C.Columns.Item[1].Font.Bold := True;
C.Columns[1].Font.Color := clBlue;
end;
end.
3. ИМПОРТ/ЭКСПОРТ ДАННЫХ ИЗ EXCEL
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls, OleServer, ExcelXP, comobj;
type
TForm1 = class(TForm)
ee: TExcelApplication;
Button1: TButton;
StringGrid1: TStringGrid;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
// Импорт данных из Excel в StringGrid
var i: integer; s1,s2: string;
begin
StringGrid1.ColWidths[0]:=120; StringGrid1.ColWidths[1]:=320;
ee.Workbooks.Add('D:\Report11.xls ', 0);
ee.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate;
StringGrid1.RowCount:=ee.ActiveCell.Row;
for i:=0 to ee.ActiveCell.Row do begin
s1:=ee.Cells.Item[8+i, 2]; s2:=ee.Cells.Item[8+i, 3];
if s1='' then break;
StringGrid1.Cells[0, i]:=s1; StringGrid1.Cells[1, i]:=s2;
end; StringGrid1.RowCount:=i; ee.Disconnect;
end;
procedure TForm1.Button2Click(Sender: TObject);
// Экспорт данных из StringGrid в Excel
var e,r: Variant; i: integer;
begin
e:=CreateOleObject('Excel.Application');
e.Visible:=true; e.WorkBooks.Add;
r:=e.Workbooks[1].WorkSheets[1];
r.Columns[1].ColumnWidth := 12;
r.Columns[2].ColumnWidth := 22;
for i:=0 to StringGrid1.RowCount-1 do begin
e.Cells.Item[i+1, 2]:=StringGrid1.Cells[0, i];
e.Cells.Item[i+1, 3]:=StringGrid1.Cells[1, i];
end;
//e.Quit;
end;
end.
