Delphi: нотатки програміста, Різне, Програмування, статті

Про файл проекту


Відомо, що Delphi створює безліч файлів з розширеннями pas, dfm, dpr і т.д. Зазвичай програміста цікавлять файли коду (pas) та опис форм (dfm), а інші залишаються поза його увагою, а дарма – Вони несуть важливу інформацію, яку можна (а в ряді випадків – потрібно) змінювати.


Почнемо з файлу проекту (dpr). Фактично він містить власне програму, в той час як пов’язані з проектом численні файли форм і DLL є підпрограми.


Структура файлу проекту, що містить єдину форму, така, як показано в лістингу 1.

program Project1;
 
uses
  Forms,
  Unit1 in Unit1.pas {Form1} ;
 
{$R *.RES}
 
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Проект (програма) починається зверненням до методу Initialize глобального об’єкта Application і закінчується після завершення його методу Run. Цей метод – центральний: він візуалізує головну форму додатка і здійснює диспетчеризацію повідомлень Windows. Від того, яка робота виконується рядками коду, розташованими перед зверненням до нього, залежать час завантаження програми, і вигляд екрану до появи головного вікна.


Метод Initialize передбачений, як то кажуть, про всяк випадок і за замовчуванням нічого не здійснює. Щоб змусити його працювати, слід помістити вказівник на відповідну процедуру без параметрів в глобальну змінну InitProc. Це можна зробити в секції ініціалізації будь-якого модуля (код цих секцій активізується до початку роботи основної програми) або безпосередньо в тілі головного проекту (Лістинг 2):

procedure Init;
begin / / Виконує якусь роботу;
end;
 
begin
  InitProc := @Init; Application.Initialize; / / Звернення до Init
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Зрозуміло, в цьому останньому випадку буде простіше в першому операторі основної програми явно звернутися до процедури Init. Взагалі кажучи, в переважній більшості випадків оператор Application.Initialize можна видалити без будь-яких наслідків для програми.


Скорочення часу завантаження


У реальному проекті з безліччю форм між зверненнями до Initialize і Run зазвичай розташовані численні виклики методу CreateForm, за допомогою якого створюються використовувані в проекті форми. Кожен виклик цього методу збільшує час завантаження програми. У початковий момент на екрані буде видно тільки одне вікно головної форми додатка (воно створюється першим зверненням до CreateForm), а решта об’єкти-вікна можна створювати в ході роботи програми перед візуалізацією відповідного вікна.


За замовчуванням в перемикачі Auto create forms (викликається за допомогою опції Project / Options середовища Delphi) на закладці Preference вікна Tools / Environment встановлений прапорець, що змушує Delphi поміщати будь-яку нову форму в список Auto-create forms і формувати відповідний оператор звернення до CreateForm в файлі проекту. Якщо очистити цей перемикач перед початком роботи над проектом або перенести непотрібні форми в список Available forms вікна Project / Options, можна істотно (іноді – в десятки разів) скоротити час завантаження програми.


Вид екрану до появи головного вікна


В наводиться нижче лістингу 3 (приклад запозичений з довідкової системи Delphi) в програмі створюється 5 вікон. У формі Form5 є індикатор ProgressBar1, за допомогою якого візуалізується процес завантаження програми, точніше – створення інших вікон.

begin
  with TForm5.Create(nil) do
  try
    ProgressBar1.Max := 100; Show; / / Показуємо форму Form5 з індикатором ProgressBar1 Update; / / Прорисовуємо форму Form5
    Application.CreateForm(TForm1, Form1);
    ProgressBar1.StepBy(25);
    Application.CreateForm(TForm2, Form2);
    ProgressBar1.StepBy(25);
    Application.CreateForm(TForm3, Form3);
    ProgressBar1.StepBy(25);
    Application.CreateForm(TForm4, Form4);
    ProgressBar1.StepBy(25);
  finally Free; / / Руйнуємо непотрібну форму Form5
  end;
  Application.Run;
end.

Захист програми паролем


Як видно з прикладу, програміст може помістити будь-який код до звернення до методу Application. Run. Зокрема, він може показати діалогове вікно із запитом пароля і блокувати виклик Application.Run , Якщо введений користувачем пароль невірний. У наступному прикладі (лістинг 4) у проекті використовуються дві форми: стандартна форма InputQuery і головна форма MainForm. Форма InputQuery створюється при зверненні до однойменної функції, визначеної в модулі Dialogs. Вона являє собою невелике діалогове вікно з однорядковим редактором TEdit і двома кнопками – ОК і Cancel. У вікні користувач повинен ввести пароль (Delphi) і натиснути Enter.

program Password;
 
uses
  Forms, Dialogs, / / ​​В цьому модулі визначена функція InputQuery
  Unit1 in Unit1.pas {MainForm} ;
 
{$R *.RES}
var
  Passwrd: String;
begin / / Запитує пароль:
  if InputQuery (Вікно введення пароля, введіть пароль:, Passwrd) then / / Перевіряємо його:
  if Passwrd = Delphi then
  begin/ / Все в порядку, пароль вірний
    Application.CreateForm(TMainForm, MainForm);
    Application.Run;
  end else ShowMessage (Пароль не вірний!);
end.

Пробні версії програм


Вищеописаним способом можна створювати пробні версії програм, які будуть функціонувати тільки до певної дати або до вичерпання заданого кількості запусків. У лістингу 5 представлений файл проекту, в якому пробна версія програми може запускатися не більше 5 раз. Для запам’ятовування номера чергового прогону використовується системний реєстр.


Тут слід дати невеликий коментар. Модуль Registry декларує клас TRegistry, який представляє програмісту доступ до системного реєстру Windows. За допомогою двох звернень до функції TRegistry.OpenKey створюється і / або відкривається ключ HKEY_CURRENT_USERSoftwareTrialProg системного реєстру. Функція TRegistry.ValueExists повертає True, якщо цей ключ містить параметр з ім’ям MaxRun і для нього визначено значення. При першому запуску програми це не так, тому процедурою WriteInteger створюється параметр і для нього вказується початкове значення 5 (максимальна кількість прогонів програми). При кожному наступного запуску цей параметр зменшується на 1 і в момент, коли він стає дорівнює 0, додаток блокує створення і відображення головного вікна.

program Trial;
 
uses
  Forms,
  Unit1 in Unit1.pas {Form1} , Registry, Dialogs; / / Для TRegistry і ShowMessage
 
{$R *.RES}
var
  Reg: TRegistry;
  N: Integer;
begin
  Reg := TRegistry.Create;
  with Reg do
  begin
    OpenKey(software, True);
    OpenKey(TrialProg, True);
    if ValueExists(MaxRun) then/ / Перший запуск?
    begin/ / – Ні
      N := ReadInteger(MaxRun)-1;
      if N>=0 then
        WriteInteger(MaxRun, N)
    end else begin/ /-Та, перший запуск
      N := 5;
      WriteInteger(MaxRun, N)
    end;
    Free
  end;
  if N>0 then
  begin
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end else ShowMessage (вичерпано максимальна кількість запусків + пробної версії програми)
end.

Вставка пояснювальних коментарів до імен форм


Зверніть увагу на пропозицію uses в тексті проекту. При перерахуванні нестандартного модуля Uni1 за допомогою зарезервованого слова in вказується ім’я файлу з текстом модуля (Unit1.pas), а наступний за ім’ям коментар {Form1} іменує об’єкт-вікно, створюваний модулем Unit1. Такого роду оголошення Delphi автоматично створює для кожного включеного в проект модуля. Delphi вважає входять в проект тільки перераховані в цьому реченні модулі, і їх алфавітний список з’являється при виборі опції View / Units; а при виборі View / Forms показується список всіх перерахованих в коментарях об’єктів.


Остання обставина можна використовувати для вставки короткого коментаря, яке зазначає призначення форми: при розробці складного проекту в нього зазвичай включається багато десятків, а іноді й сотень форм, і подібна можливість може виявитися зовсім не зайвою. Як приклад на рис. 1 показана копія екрану, що відображає файл проекту з коментарями, і відповідне вікно View Form.


Про перенесення проекту в іншу папку


До речі, даний малюнок ілюструє порушення правила «один проект – одна папка»: у цьому проекті, всупереч зазначеному правилу, багато модулі зберігаються у вкладених папках, в результаті Delphi вказує довгі маршрути доступу до відповідних файлів. Якщо такий проект скопіювати на дискету, то ці маршрути залишаться без зміни і компілятор не зможе знайти потрібні файли. Ще гірше, якщо скопіювати проект в іншу папку на тому ж жорсткому диску. В цьому випадку почнуться неприємності: ви будете щось змінювати у новому проекті, але додаток на це ніяк не відреагує, а якщо ви встановите контрольну точку останова в будь-якому з модулів, то вона виявиться непрацездатною – компілятор буде як і раніше використовувати оригінальні файли, а не копії.


Якщо ви захочете перенести проект в іншу папку і при цьому зберегти його працездатність, вам потрібно спочатку за допомогою опції File / Save Project As скопіювати в цю папку файл проекту, а потім з допомогою опції File / Save As перенести туди все пов’язані з проектом модулі: тільки тоді Delphi зуміє внести необхідні корекції в файл проекту. Але якщо всі файли зберігаються в єдиній папці, то в реченні uses не вказуються маршрути доступу, і тому ви зможете безболісно скопіювати разом все файли в іншу папку.


Настроювальний файл. Dsk, в якому середу зберігає інформацію про стан екрану в момент виходу з Delphi, також містить повні маршрути доступу до відкритих файлів. При перенесенні проекту цей файл копіювати не слід.


Урізноманітимо висновок повідомлень


Все багатство образотворчих можливостей Windows зовсім не вичерпується набором властивостей і методів класу TCanvas: цей клас інкапсулює лише найбільш популярні прийоми роботи з креслярськими інструментами. У табл. 1 перераховані деякі функції Windows, які не інкапсулює клас TCanvas і здатні значно урізноманітнити текстовий висновок (саме він найбільш обеднен вузькими рамками TCanvas).






































type TLogFont = record


  lfHeight: Integer; lfWidth: Integer; lfEscapment: Integer; lfOrientation: Integer; lfWeight: Integer; lfItalic: Byte; lfUnderline: Byte; lfStrikeOut: Byte; lfCharSet: Byte;  lfOutPrecision: Byte; lfClipPrecision: Byte; lfQuality: Byte; lfPitchAndFamily: Byte; lfFaceName: PChar;


end;


function CreateFont(Font: TLogFont): hFont;


Створює новий шрифт на основі даних у параметрі Font (призначення полів структури TLogFont см. в тексті після таблиці)


function DrawText(DC: hDC; pText: PChar; var Rect: TRect; Format: Wodr): Integer;


В прямокутнику Rect виводить багаторядковий текст, на який вказує pText. Параметр Format використовується для форматування (див. нижче)


function ExtTextOut(DC: hDC; X, Y: Integer; Options: Integer; Rect: TRect; pText: PChar; Count: Integer; PX: PInteger): Bool;


Виводить текст з нестандартними міжсимвольним відстанями: X, Y – верхня ліва точка тексту; Options – параметр, що керує виведенням (див. нижче); Rect – обмежує прямокутник; pText – покажчик на рядок виводу; Count – кількість виведених символів; PX – покажчик на масив цілочисельних значень, що визначають міжсимвольні відстані: 1-й параметр – відстань від 1-го до 2-го символу; 2-й параметр – Відстань від 2-го до 3-го символу і т.д.; якщо якийсь параметр дорівнює 0, використовується замовчувана міжсимвольні відстань


function GetBkColor(DC: hDC): TColor;


Повертає колір фону


function GetBkMode(DC: hDC): Integer;


Повертає режим промальовування фону: Opaque – фон прорисовується заново при виведенні тексту; Transparent – фон не промальовується.


function GetTextAlign(DC: hDC): Integer;


Повертає вирівнювання тексту


function GetTextCharacterExtra(DC: hDC): Integer;


Повертає міжсимвольні відстань


function SetBkColor(DC: hDC; Color: TColor): TColor;


Встановлює новий колір фону і повертає старий, якщо звернення успішно


function SetBkMode(DC: hDC; Mode: Integer): Integer;


Встановлює новий режим промальовування фону і повертає старий, якщо операція успішна


function SetTextAlign(DC: hDC; Flags: Integer): Integer;


Встановлює нове вирівнювання тексту та повертає старе, якщо виклик успішний


function SetTextCharacterExtra(DC: hDC; CharExtra: Integer): Integer;


Встановлює нове міжсимвольні відстань і повертає старе, якщо виклик успішний


Окремі поля структури TLogFont для функції CreateFontIndirect мають наступний зміст:


lfHeight – висота шрифту в пунктах (1 пункт = 1/72 дюйма); якщо більше 0 – визначає висоту «знакоместа» (з урахуванням виступаючих над заголовним символом елементів в буквах Е, Й); якщо менше 0 – визначає висоту «чистого» символу, якщо дорівнює 0 – висоту вибирає Windows;


lfWidth – середня ширина символу; якщо дорівнює 0 – ширину встановлює Windows;


lfEscapment – кут нахилу базової лінії тексту в десятих частках градуса щодо горизонтального напрямку; позитивні значення – поворот по годинникової стрілки; в Windows 95/98 співпадає з lfOrientation;


lfOrientation – кут нахилу символів по відношенню до базової лінії; в Windows NT для шрифтів True Type може відрізнятися від lfEscapment; для цього слід встановити режим пристрою відображення рівним gm_Advanced (за умовчанням встановлюється gm_Compatible);


lfWeight – щільність шрифту (fm_DontCare = 0 – щільність вибирає Windows; fm_Thin = 100 – дуже тонкий шрифт; fm_ExtraLight = 200 – дуже світлий; fm_Light = 300 – світлий; fm_Normal = 400 – нормальний; fm_Medium = 500 – Потовщений; fm_SemiBold = 600 – напівжирний; fm_Bold = 700 – жирний; fm_ExtraBold = 800 – посилений; fm_Heavy = 900 – важкий);


lfItalic, lfUnderline, lfStrikeOut – ненульове значення означає відповідно похилий, перекреслений і підкреслений шрифт;


lfCharSet – набір символів (ANSI_CharSet = 0; Default_CharSet = 1; Symbol_CharSet = 2; ShiftJis_CharSet = 128; OEM_CharSet = 255);


lfOutPrecision – точність представлення шрифту; рекомендується Out_TT_Prec (вибирає True Type і векторні шрифти, якщо є кілька різновидів однойменних шрифтів) або Out_TT_Only_Prec (тільки True Type);


lfClipPrecision – визначає точність відсікання написи межами області промальовування (Clip_Character_Precis, Clip_Embedded, Clip_Mask, Clip_TT_Always, Clip_Default_Precis – рекомендується, Clip_LH_Angles , Clip_Stroke_Precis );


lfQuality – визначає якість промальовування (Default_Quality, Draft_Quality, Proof_Quality);


lfPitchAndFamily – в чотирьох молодших розрядах вказується тип шрифту, в чотирьох старших – його сімейство;


lfFaceName – ім’я гарнітури шрифту.


На рис. 2 показаний приклад виведення похилими шрифтами, створеними функцією CreateFontIndirect.


Як реалізований цей приклад, показано в лістингу 6.

procedure TForm1.FormPaint(Sender: TObject);
var
  X: Integer;
  LF: TLogFont;
  Fnt: HFont;
const Text = Краща в світі система програмування;
begin / / Визначаємо параметри нового шрифту
  FillChar(LF, SizeOf(LF), 0);
  with LF do
  begin
    lfHeight := 20;
    lfWeight := fw_Normal;
    lfUnderline := 1;
    lfEscapement := 450;
    StrPCopy(lfFaceName, Courier New Cyr);
  end;
  with Form1.Canvas do
  begin / / Створюємо шрифт
    Fnt := CreateFontIndirect(LF); / / Надаємо його дескриптор шрифту канви
    Font.Handle := Fnt; / / Виводимо текст під кутом +45 градусів
    TextOut(0, 300, Text);
    X := TextWidth(Text); DeleteObject (Fnt); / / Видаляємо непотрібний шрифт / / Змінюємо параметри шрифту
    with LF do
    begin
      lfHeight := 90;
      lfEscapement := -900;
      lfWeight := fw_Heavy;
      StrPCopy(LF.lfFaceName, Arial Cyr);
    end; Fnt: = CreateFontIndirect (LF); / / Створюємо новий шрифт
    Font.Handle := Fnt;
    Font.Color := clRed; / / Виводимо з нахилом -90 градусів
    TextOut(X-10, 10, Delphi 5); DeleteObject (Fnt); / / Видаляємо непотрібний шрифт
  end;
end;

Параметр Format функції DrawText може містити один або кілька прапорів (табл. 2).




















































Прапор Призначення
dt_Bottom Текст притискається до нижньої частини області Rect
dt_CalcRect Дозволяє динамічно змінювати розміри області Rect
dt_Center Текст центрується по горизонталі
dt_EditControl Функція дублює властивості відображення багаторядкового тестового редактора. Зокрема, таким же способом обчислюється середня ширина символу і не показується частково видима останній рядок
dt_ExpandTabs Символи табуляції замінюються пробілами
dt_ExternalLeading У висоту рядка включається висота міжрядкового інтервалу
dt_Left Текст притискається до лівої частини області Rect
dt_NoClip Текст не відсікається кордонами Rect
dt_NoPrefix Символи & не замінюються підкресленням
dt_Right Текст притискається до правої частини області Rect
dt_SingleLine Весь текст виводиться єдиною рядком, символи EOLN ігноруються
dt_TabsStop Символи табуляції не замінюються пробілами
dt_Top Текст притискається до верхньої частини області Rect
dt_VCenter Текст центрується по вертикалі
dt_WordBreak Дозволяє перехід на новий рядок при досягненні правої межі Rect; розрив рядка – на кордоні слова; символи EOLN також переводять висновок на наступний рядок



Якщо встановлений прапор dt_CalcRect, функція змінює висоту і ширину прямокутника таким чином, щоб вивести весь текст. Якщо виводиться кілька рядків, ширина висновку не змінюється. Функція повертає справжню висоту прямокутника виводу.


Параметр Options функції ExtTextOut може бути комбінацією наступних значень:


eto_Clipped – текст буде відсікатися кордонами Rect;


eto_Gliph_Index – блокує обробку мовним драйвером;


eto_Opaque – фон перемальовується заново;


eto_RTLReading – висновок для читання справа наліво.


При виведенні тексту стандартними методами TCanvas завжди заново прорисовується тло символів (колір фону повертає функція GetBkColor). Якщо колір фону символів відрізняється від фону канви, висновок супроводжується неприємними побічними ефектами. Якщо в прикладі з лістингу 6 для форми залишити стандартний колір clFaceBtn, вікно виведення буде таким, як на рис. 3.


Звичайно, за допомогою функції SetBkColor можна встановити, щоб колір форми збігався з кольором канви, проте це не завжди можливо. Для прикладу на рис. 4 показаний екран, який традиційно створюють багато програм установки Setup.exe. Фон на такому екрані не залишається постійним, а плавно переходить від інтенсивного синього до чорного. Ясно, що встановити змінний колір функцією SetBkColor неможливо.


Більш того, великі написи програм Setup.exe також традиційно виводяться потовщеним похилим шрифтом Times New Roman білими буквами з чорною тінню. Реалізувати подібний ефект досить просто. Так, потрібно взагалі відмовитися від промальовування фону, встановивши за допомогою функції SetBkMode режим Transparent, і вивести напис двічі: перший раз чорним кольором, а другий – білим, змістивши другий напис трохи вліво і вгору щодо першої. Лістинг 7 ілюструє сказане:

procedure TForm1.FormPaint(Sender: TObject);
var
  Y: Integer;
  Blue: Byte;
const Text = Фон для програми Setup.exe;
begin
  with Form1.Canvas do
  begin / / Створюємо фон:
    for Y := 0 to Form1.Height-1 do
    begin / / Зменшуємо інтенсивність кольору з ростом ординати Y
      Blue := Round($FF*(Form1.Height-Y)/Form1.Height); / / Формуємо колір
      Pen.Color := RGB(0, 0, Blue); / / Креслимо лінію
      MoveTo(0, Y);
      LineTo(Form1.Width-1, Y);
    end;  // for Y := 0 to Form1.Height-1 do
    Font.Size := 32;
    Font.Style := [fsBold, fsItalic, fsUnderline];
    Font.Name := Times New Roman Cyr; / / Це звернення накладає текст на фон:
    SetBkMode(Handle, Transparent); / / Спочатку виводимо тінь написи
    Font.Color := clBlack;
    TextOut(40, 30, Text); / / Тепер саму напис
    Font.Color := clWhite;
    TextOut(36, 26, Text)
  end;  //  with Form1.Canvas do
end;

На рис. 4 показано вікно працюючої програми.


Включення до бібліотеки форм


Незважаючи на те що DLL не має власної форми, з її допомогою можна викликати форми з пов’язаних з бібліотекою модулів. Для цього в бібліотеці використовується посилання uses на пов’язані модулі-форми і оголошуються експортуються з DLL підпрограми, в яких реалізується виклик відповідних форм.


У наступних прикладах ілюструється техніка включення в DLL форми і використання її в зухвалій програмі (листинги 8, 9 і 10).


Лістинг 8. Текст DLL

 library DLLWithForm;
 
uses
  SysUtils,
  Classes,
  DLLFormU in DLLFormU.pas {DLLForm} ;
 
{$R *.RES}
 
exports
  ShowModalForm, ShowForm, FreeForm;
 
begin
end.

Лістинг 9. Текст форми в DLL

unit DLLFormU;
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;
 
type
  TDLLForm = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations } CallForm: THandle; / / Дескриптор викликає форми
 public
    { Public declarations }
  end;

Лістинг 10. Текст зухвалої програми

unit TestMainU;
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
 
type
  TTestMain = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
      procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations } / / Наступна процедура обробляє повідомлення WM_USER, / / Яке посилає форма з DLL в момент свого закриття
   procedure WMUser(var Msg: TMessage);  message WM_USER;
 end;
 
var
  TestMain: TTestMain;
 
implementation
 
{$R *.DFM}
 
function ShowModalForm: Integer; External DLLWithForm;
procedure ShowForm(Appl, Form: THandle); External DLLWithForm;
procedure FreeForm; External DLLWithForm;
 
procedure TTestMain.Button1Click(Sender: TObject); / / Модальний виклик
begin
  Button2.Enabled := False;
  label1.Caption := ModalResult = +IntToStr(ShowModalForm); label1.Show; / / Показуємо результат виклику
  Button2.Enabled := True
end;
 
procedure TTestMain.Button2Click(Sender: TObject); / / Немодальний виклик
begin
  Button1.Enabled := False;
  Button2.Enabled := False;
  Button3.Enabled := True;
  label1.Hide;
  ShowForm(Application.Handle, Self.Handle);
end;
 
procedure TTestMain.Button3Click(Sender: TObject); / / Закрити форму
begin
  FreeForm;
  Button1.Enabled := True;
  Button2.Enabled := True;
  Button3.Enabled := False;
end;
 
procedure TTestMain.WMUser(var Msg: TMessage); / / Повідомлення з форми DLL про її закриття
begin
  Button3.Click
end;
 
end.

Модуль форми DLLForm, вміщеній в DLL, посилається на стандартний модуль Forms і, таким чином, отримує свій глобальний об’єкт Application, який нічого «не знає» про глобальне об’єкті зухвалої програми. У режимі модального виклику це не має особливого значення, оскільки модальне вікно блокує роботу зухвалої програми. В режимі немодального виклику слід синхронізувати дії об’єктів, в іншому випадку мінімізація головного вікна, наприклад, не призведе до мінімізації вікна DLL. Синхронізація досягається тим, що дескриптор об’єкта Application DLL замінюється відповідним дескриптором викликає програми.


При показі форми в немодальному режимі вона може бути закрита або що викликала її програмою, або клацанням по власній системній кнопці Закрити. В останньому випадку вона повинна якимось чином сповістити зухвалу програму про цю подію. Для цього використовується стандартний механізм посилки Windows-повідомлення. Повідомлення повинно мати адресу – дескриптор вікна, для якого воно призначене. Ось чому другим параметром звернення до функції ShowForm в DLL передається і в полі CallForm запам’ятовується дескриптор вікна зухвалої програми. Обробник події OnClose форми перевіряє це поле і, якщо воно визначено, посилає викликав вікна повідомлення з індексом WM_USER. У зухвалій програмі передбачений обробник цього повідомлення, в якому реалізуються необхідні дії.

Друк в Delphi


Об’єкт Printer автоматично створюється в разі, якщо в програмі зазначене посилання на модуль Printers. Цей об’єкт надає програмісту все необхідне для того, щоб навчити додаток виводити дані на один з підключених до комп’ютера принтерів.


Висновок на принтер в Windows нічим не відрізняється від виведення на екран: у розпорядження програміста надається властивість Canvas об’єкта Printer, що містить набір креслярських інструментів, і методи, властиві класу TCanvas. Розмір аркуша паперу в пікселах визначають властивості Height і Width, а набір принтерних шрифтів – властивість Fonts.


Друк тексту


Існує безліч способів друку тексту на принтері. Перш за все слід назвати глобальну процедуру AssignPrn (вона визначена в модулі Printers), що дозволяє використовувати принтер як текстовий файл і друкувати текстові рядки з допомогою процедури WriteLn. У лістингу 1 (PrintText.dpr) наведено повний текст модуля, на формі якого розташовані багаторядковий текстовий редактор Memo1 і чотири кнопки: для вибору текстового файлу і введення його вмісту в редактор, для вибору потрібного шрифту відображення / друку документа, для ініціації процесу друку та для завершення роботи програми.


unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;

type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
BitBtn1: TBitBtn;
Button3: TButton;
FontDialog1: TFontDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses Printers; / / Це посилання обов’язкове!

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
/ / Вибір файлу з текстом і його завантаження в редактор
begin
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName)
end;

procedure TForm1.Button3Click(Sender: TObject);
/ / Вибір шрифту і зв’язування його з Memo1
begin
if FontDialog1.Execute then
Memo1.Font := FontDialog1.Font
end;

procedure TForm1.Button2Click(Sender: TObject);
/ / Друк вмісту редактора як висновок в текстовий файл
var
Prn: TextFile;
k: Integer;
begin
AssignPrn (Prn); / / перепризначає висновок у файл на висновок в принтер
Rewrite (Prn); / / Готуємо принтер до друку (аналог BeginDoc)
{Для друку використовуємо такий же шрифт, як і для показу
в редакторі:}
Printer.Canvas.Font := Memo1.Font;
/ / Цикл друку:
for k := 0 to Memo1.Lines.Count-1 do
WriteLn(Prn, Memo1.Lines[k]);
CloseFile (Prn); / / Аналог EndDoc
end;

end.


Описаний спосіб друку – найпримітивніший: з його допомогою неможливо вивести лінії, що розділяють колонки або рядка, важко форматувати текст, вставляти заголовки, номери сторінок і т.д.


Значно більш гнучкі засоби забезпечує властивість Printer.Canvas. Покажемо, як з його допомогою можна надрукувати текст, що міститься в редакторі Memo1 (PrintText.dpr, лістинг 2):


unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;

type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
BitBtn1: TBitBtn;
Button3: TButton;
FontDialog1: TFontDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses Printers; / / Це посилання обов’язкове!

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
/ / Вибір файлу з текстом і його завантаження в редактор
begin
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName)
end;

procedure TForm1.Button3Click(Sender: TObject);
/ / Вибір шрифту і зв’язування його з Memo1
begin
if FontDialog1.Execute then
Memo1.Font := FontDialog1.Font
end;

procedure TForm1.Button2Click(Sender: TObject);
/ / Друк вмісту редактора як висновок в текстовий файл
var
Prn: TextFile;
k: Integer;
begin
AssignPrn (Prn); / / перепризначає висновок у файл на висновок в принтер
Rewrite (Prn); / / Готуємо принтер до друку (аналог BeginDoc)
{Для друку використовуємо такий же шрифт, як і для показу
в редакторі:}
Printer.Canvas.Font := Memo1.Font;
/ / Цикл друку:
for k := 0 to Memo1.Lines.Count-1 do
WriteLn(Prn, Memo1.Lines[k]);
CloseFile (Prn); / / Аналог EndDoc
end;

end.


Як можна побачити, пряме звернення до креслярським інструментів властивості Canvas вимагає від програміста значно більших зусиль, але зате надає йому повний контроль над друкарським зображенням.


У багатьох випадках для друку документа і внесення до нього елементарних засобів форматування (друк загального заголовка, заголовка на кожній сторінці, номерів сторінок і т.п.) простіше використовувати спеціальні компоненти, розташовані на сторінці QReport палітри компонентів Delphi. Ці компоненти розроблені для створення звітів по базах даних, але можуть з успіхом використовуватися і для друку звичайних документів (PrintText.dpr).


Нарешті, дуже хороших результатів можна досягти, використовуючи спеціалізовані засоби перегляду / друку документів, як, наприклад, текстовий процесор MS Word.


Друк зображень


Друк зображень може здатися дуже складною справою, проте властивість Printer.Canvas містить метод:


procedure StretchDraw(const Rect: TRect; Graphic: TGraphic );


який легко справляється з цим завданням. При зверненні до нього в якості першого параметра вказується прямокутна область, відведена на поверхні листа для роздруківки зображення, а в якості другого – Об’єкт класу TGraphic, в якому зберігається зображення, наприклад:


with Printer do
begin
BeginDoc;
Canvas.StretchDraw(Canvas.ClipRect, Image1.Picture.Graphic);
EndDoc;
end;


Відображення файла в пам’ять


Для роботи з файлом динамічної підкачки сторінок віртуальної пам’яті в Windows 32 використовується механізм відображення файлів в адресний простір програми. Відповідні функції API доступні будь-якому додатком і можуть застосовуватися до будь-якого файлу (до речі, таким способом завантажуються в адресний простір процесу виконувані файли і DLL). В результаті відображення додаток може працювати з файловими даними як з розміщеними в динамічній пам’яті. У більшості випадків така можливість не тільки підвищує швидкість роботи з даними, а й надає програмісту унікальні засоби обробки відразу всіх записів файлу. Наприклад, він може за допомогою єдиного оператора перевірити, чи входить заданий зразок пошуку в яку-небудь рядок текстового файлу.


Відображення файла здійснюється в три прийоми. Спочатку файл створюється зверненням до функції:


function FileCreate (FileName: String): Integer;


або відкривається за допомогою:


function FileOpen (const FileName: String; Mode: LongWord): Integer;


В обох функціях FileName – ім’я файлу, можливо, з маршрутом доступу. Параметр Mode визначає режим доступу до файлу і може приймати одне з наступних значень: fmOpenRead – тільки читання; fmOpenWrite – Тільки запис; fmOpenReadWrite – читання і запис. За допомогою операції or ці константи можна комбінувати з однією з наступних декількох функцій, які регулюють спільний доступ до файлу: fmShareExclusive – Спільний доступ заборонений; fmShareDenyWrite – іншим додаткам забороняється запис; fmShareDenyRead – іншим додаткам забороняється читання; fmSchareDenyNone – спільний доступ необмежений. Обидві функції повертають дескриптор створеного (відкритого) файлу або 0, якщо операція виявилася невдалою.


На другому етапі створюється об’єкт відображення в пам’ять. Для цього використовується функція:


function CreateFileMapping (hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWord; lpName: PChar): THandle;


Тут hFile – дескриптор файлу; lpFileMappingAttributes – покажчик на структуру, в якій визначається, чи може створюваний об’єкт породжувати дочірні об’єкти (звичайно не може – NIL); flProtect – визначає тип захисту, що застосовується до вікна відображення файлу (див. про це нижче); dwMaximumSizeHigh, dwMaximumSizeLow – відповідно старші і молодші 32 розряду числа, що містить розмір файлу (якщо ви будете відображати файли довжиною до 4 Гбайт, помістіть в dwMaximumSizeHigh 0, якщо в dwMaximumSizeLow – довжину файлу, а якщо обидва параметри рівні 0, то розмір вікна відображення дорівнює розміру файлу); lpName – ім’я об’єкта відображення або NIL.


Параметр flProtect задає тип захисту, що застосовується до вікна перегляду файлу, і може мати одне з наступних значень: PAGE_READONLY – файл можна тільки читати (файл повинен бути створений або відкритий в режимі fmOpenRead); PAGE_READWRITE – файл можна читати і записувати в нього нові дані (файл відкривається в режимі fmOpenReadWrite); PAGE_WRITECOPY – файл відкритий для запису і читання, однак оновлені дані зберігаються в окремій захищеної області пам’яті (відображені файли можуть розділятися додатками, в цьому режимі кожен додаток зберігає зміни в окремій області пам’яті або ділянці файлу підкачки); файл відкривається в режимі fmOpenReadWrite або fmOpenWrite; (цей тип захисту не можна використовувати в Windows 95/98). За допомогою операції or до параметру flProtect можна приєднати такі атрибути: SEC_COMMIT – Виділяє для відображення фізичну пам’ять або ділянка файлу підкачки; SEC_IMAGE – інформація про атрибути відображення береться з образу файлу; SEC_NOCASHE – відображаються дані не кешуються і записуються безпосередньо на диск; SEC_RESERVE – резервуються сторінки розділу без виділення фізичної пам’яті.


Функція повертає дескриптор об’єкта відображення або 0, якщо звернення було невдалим.

Нарешті, на третьому етапі створюється вікно перегляду, тобто власне відображення даних в адресний простір програми.


function MapViewOfFile(hFileMappingObject: THandle;dwDesiresAccess: DWord; dwFileOffsetHigh, dwFileIffsetLow, dwNumberOfBytesToMap: DWord): Pointer;


Тут hFileMappingObject – дескриптор об’єкта відображення; dwDesiresAccess – визначає спосіб доступу до даних і може мати одне з наступних значень: FILE_MAP_WRITE – дозволяє читання та запис (при цьому у функції CreateFileMapping повинен використовуватися атрибут PAGE_READWRITE); FILE_MAP_READ – дозволяє тільки читання (у функції CreateFileMapping повинен використовуватися атрибут PAGE_READONLY або PAGE_READWRITE); FILE_MAP_ALL_ACCESS – те ж, що і FILE_MAP_WRITE; FILE_MAP_COPY – дані доступні для запису і читання, однак оновлені дані зберігаються в окремій захищеної області пам’яті (у функції CreateFileMapping повинен використовуватися атрибут PAGE_WRITECOPY); dwFileOffsetHigh, dwFileIffsetLow – визначають відповідно старші і молодші розряди зміщення від початку файлу, починаючи з якого здійснюється відображення; dwNumberOfBytesToMap – визначає довжину вікна відображення (0 – довжина дорівнює довжині файлу). Функція повертає вказівник на перший байт відображених даних або NIL, якщо звернення до функції виявилося безуспішним.


Після використання відображених даних ресурси вікна відображення потрібно звільнити функцією:

function UnMapViewOfFile(lpBaseAddress: Pointer): BOOL;


єдиний параметр звернення до якої повинен містити адресу першого відображеного байта, то є адреса, що повертається функцією MapViewOfFile. Закриття об’єкта відображення і самого файлу здійснюється зверненням до функції:


function CloseHandle(hObject: THandle).


У лістингу 3 наводиться текст модуля (FileInMemory.dpr), який створює вікно, показане на рис. 1.Проект створює дисковий файл, що складається з 100 тис. випадкових речових чисел (можна вибрати іншу довжину файлу, якщо змінити значення редактора Довжина масиву). Файл з ім’ям test.dat створюється шляхом відображення файлу в пам’ять (кнопка Пам’ять) і традиційним способом (кнопка Файл). В обох випадках показується час рахунку. Чим більше частота процесора й обсяг вільної оперативної пам’яті, тим більше буде різниця в часі.


unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Spin;

type
TForm1 = class(TForm)
btMem: TButton;
btFile: TButton;
se: TSpinEdit;
Label1: TLabel;
pb: TProgressBar;
Label2: TLabel;
lbMem: TLabel;
lbFile: TLabel;
procedure btMemClick(Sender: TObject);
procedure btFileClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.btMemClick(Sender: TObject);
/ / Створення файлу методом його відображення
type
PReal = ^Real;
var
HFile, HMap: THandle;
AdrBase, AdrReal: PReal;
k: Integer;
FSize: Cardinal;
BegTime: TDateTime;
begin
BegTime: = Time; / / Засікаємо час пуску
/ / Готуємо ProgressBar:
pb.Max := se.Value;
pb.Position := 0;
pb.Show;
FSize: = se.Value * SizeOf (Real); / / Довжина файлу
HFile: = FileCreate (test.dat); / / Створюємо файл
if HFile = 0 then / / Помилка: порушуємо виняток
raise Exception.Create (Помилка створення файлу);
try
/ / Відображається файл в пам’ять
HMap := CreateFileMapping(
HFile, NIL, PAGE_READWRITE, 0, FSize, NIL);
if HMap = 0 then / / Помилка: порушуємо виняток
raise Exception.Create (Помилка відображення файлу);
try
/ / Створюємо вікно перегляду:
AdrBase := MapViewOfFile(HMap, FILE_MAP_WRITE, 0, 0, FSize);
if AdrBase = NIL then / / Помилка: порушуємо виняток
raise Exception.Create (Неможливо переглянути файл);
/ / Зберігаємо початкова адреса для правильної ліквідації
/ / Вікна перегляду:
AdrReal := AdrBase;
for k := 1 to se.Value do
begin
AdrReal ^: = Random; / / Розміщуємо в файл нове число
/ / Перед нарощуванням поточного адреси необхідно
/ / Привести його до типу Integer або Cardinal:
AdrReal := Pointer(Integer(AdrReal) + SizeOf(Real));
lbMem.Caption := IntToStr(k);
pb.Position := k;
Application.ProcessMessages;
end;
/ / Звільняємо вікно перегляду:
UnmapViewOfFile(AdrBase)
finally
/ / Звільняємо відображення
CloseHandle(HMap)
end
finally
/ / Закриваємо файл
CloseHandle(HFile)
end;
/ / Повідомляємо час рахунку
pb.Hide;
lbMem.Caption := TimeToStr(Time-BegTime)
end;

procedure TForm1.btFileClick(Sender: TObject);
/ / Створення файлу звичайним методом
var
F: File of Real;
k: Integer;
BegTime: TDateTime;
R: Real; / / Буферна змінна для звернення до Write
begin
BegTime: = Time; / / Засікаємо початкове час рахунку
/ / Готуємо ProgressBar:
pb.Max := se.Value;
pb.Position := 0;
pb.Show;
/ / Створюємо файл:
AssignFile(F, test.dat);
Rewrite(F);
for k := 1 to se.Value do
begin
R: = Random; / / Параметрами звернення до Write
Write (F, R); / / можуть бути тільки змінні
lbFile.Caption := IntToStr(k);
pb.Position := k;
Application.ProcessMessages;
end;
CloseFile(F);
pb.Hide;
lbFile.Caption := TimeToStr(Time-BegTime)
end;

end.


Про таймері


Компонент Timer (таймер) служить для відліку інтервалів реального часу. Його властивість Interval визначає інтервал часу в мілісекундах, який повинен пройти від включення таймера до настання події OnTimer. Таймер вмикається при установці значення True в його властивість Enabled. Один раз включений таймер весь час буде порушувати події OnTimer до тих пір, поки його властивість Enabled не прийме значення False.


Слід врахувати, що в силу специфіки реалізації стандартного апаратного таймера IBM-сумісного комп’ютера мінімальний реально досяжний інтервал відліку часу не може бути менше 55 мс (цей інтервал називається тиком), більш того, будь-який інтервал часу, відлічуваний за допомогою таймера, завжди кратний 55 мс. Щоб переконатися в цьому, проведіть експеримент, в якому підраховується середній час між двома спрацьовуваннями таймера (Timer.dpr):



  1. Почніть новий проект з порожньою формою і покладіть на неї компонент TTimer.

  2. Встановіть у властивість Enabled таймера значення False.

  3. Напишіть такий модуль головної форми (лістинг 4):

unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls;

type
TfmExample = class(TForm)
Panel1: TPanel;
bbRun: TBitBtn;
bbClose: TBitBtn;
edInput: TEdit;
lbOutput: TLabel;
mmOutput: TMemo;
Timer1: TTimer;
procedure bbRunClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
BegTime: TDateTime; / / Початкове час циклу
Counter: Integer; / / Лічильник циклу
public
{ Public declarations }
end;

var
fmExample: TfmExample;

implementation

{$R *.DFM}

procedure TfmExample.bbRunClick(Sender: TObject);
/ / Запускає таймер. edInput містить період його спрацьовування.
var
Delay: Word;
begin
/ / Перевіряємо завдання інтервалу
if edInput.Text= then Exit;
try
Delay := StrToInt(edInput.Text);
except
ShowMessage (Помилка в запису числа);
edInput.SelectAll;
edInput.SetFocus;
Exit
end;
Counter: = 0; / / Скидаємо лічильник
Timer1.Interval: = Delay; / / Встановлюємо інтервал
BegTime: = Time; / / Засікаємо час
Timer1.Enabled: = True; / / Пускаємо таймер
Screen.Cursor := crHourGlass
end;

procedure TfmExample.Timer1Timer(Sender: TObject);
var
h, m, s, ms: Word; / / Змінні для декодування часу
const
MaxCount = 55; / / Кількість спрацьовувань таймера
begin
Counter: = Counter + 1; / / Нарощуємо лічильник спрацьовувань
if Counter = MaxCount then / / Кінець циклу?
begin / / – Так
Timer1.Enabled: = False; / / Зупиняємо таймер
/ / Знаходимо середній час спрацьовування:
DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms);
mmOutput.Lines.Add (/ / Виводимо результат
Format (Встан.% s ms. Отримано% d ms., [EdInput.Text, ms]));
edInput.Text: =; / / Готуємо наступний запуск
edInput.SetFocus;
Screen.Cursor := crDefault
end;
end;

procedure TfmExample.FormActivate(Sender: TObject);
begin
edInput.SetFocus
end;

end.
Необхідність декількох (MaxCount) спрацьовувань для точного усереднення результату пов’язана з тим, що системні годинник оновлюються кожні 55 мс. Після запуску програми і введення 1 як необхідного періоду спрацювання в редакторі mmOutput ви побачите рядок

Визнач 1 ms. Отримано 55 ms. в якій вказується, який реальний час розділяє два сусідніх події OnTimer. Якщо ви встановите період таймера в діапазоні від 56 до 110 мс, у рядку буде вказано 110 ms і т.д. (В силу дискретності поновлення системних годин результати можуть дещо відрізнятися в ту чи іншу сторону).

У ряді практично важливих областей застосування (при розробці ігор, в системах реального часу для управління зовнішніми пристроями і т.п.) інтервал 55 мс може виявитися занадто великий. Сучасний ПК має мультимедійний таймер, період спрацювання якого може бути від 1 мс і вище, проте цей таймер не має компонентного втілення, тому для доступу до нього доводиться використовувати функції API.


Загальна схема його використання така. Спочатку готується процедура зворотного виклику (call back) з заголовком:


procedure TimeProc(uID, uMsg: UINT; dwUser, dw1, dw2: DWORD); stdcall;


Тут uID – ідентифікатор події таймера (див. про це нижче); uMsg – не використовується; dwUser – довільне число, яке передається процедурі в момент спрацьовування таймера; dw1, dw2 – не використовуються.


Запуск таймера реалізується функцією:


function timeSetEvent(uDelay, uResolution: UINT; lpTimeProc: Pointer; dwUser: DWORD; fuEvent: UINT): UINT; stdcall; external winmm.dll;


Тут uDelay – необхідний період спрацювання таймера (в мс); uResolution – дозвіл таймера (значення 0 означає, що події спрацьовування таймера будуть виникати з максимально можливою частотою; в метою зниження навантаження на систему ви можете збільшити це значення); lpTimeProc – адреса процедури зворотного виклику; dwUser – довільне число, яке передається процедурі зворотного виклику і яким програміст може розпоряджатися на свій розсуд; fuEvent – параметр, що керує періодичністю виникнення події таймера: TIME_ONESHOT (0) – подія виникає тільки один раз через uDelay мілісекунд; TIME_PERIODIC (1) – події виникають періодично кожні uDelay мс. При успішному зверненні функція повертає ідентифікатор події таймера і 0, якщо звернення було помилковим.


Таймер зупиняється, і пов’язані з ним системні ресурси звільняються функцією:


function timeKillEvent(uID: UINT): UINT; stdcall; external winmm.dll;


Тут uID – ідентифікатор події таймера, отриманий за допомогою timeSetEvent.


У наступному прикладі (Timer.dpr) ілюструється використання мультимедійного таймера (лістинг 5).


unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls;

type
TfmExample = class(TForm)
Panel1: TPanel;
bbRun: TBitBtn;
bbClose: TBitBtn;
edInput: TEdit;
lbOutput: TLabel;
mmOutput: TMemo;
procedure bbRunClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
fmExample: TfmExample;

implementation

{$R *.DFM}
/ / Оголошення експортованих функцій:

function timeSetEvent(uDelay, uReolution: UINT; lpTimeProc: Pointer;
dwUser: DWORD; fuEvent: UINT): Integer; stdcall; external winmm;

function timeKillEvent(uID: UINT): Integer; stdcall; external winmm;

/ / Оголошення глобальних змінних
var
uEventID: UINT; / / Ідентифікатор події таймера
BegTime: TDateTime; / / Засікаємо час <
Counter: Integer; / / Лічильник повторень
Delay: Word; / / Період спрацьовування

procedure ProcTime(uID, msg: UINT; dwUse, dw1, dw2: DWORD); stdcall;
/ / Реакція на спрацьовування таймера (процедура зворотного виклику)
var
h, m, s, ms: Word; / / Змінні для декодування часу
const
MaxCount = 55; / / Кількість повторень
begin
timeKillEvent (uEventID); / / Зупиняємо таймер
Counter: = Counter +1; / / Нарощуємо лічильник
if Counter = MaxCount then / / Кінець циклу?
begin / / – Так: декодуємо час
DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms);
fmExample.mmOutput.Lines.Add (/ / Повідомляємо результат
Format (Встан.% s ms. Отримано% d ms,
[fmExample.edInput.Text,ms]));
fmExample.edInput.Text: =; / / Готуємо повторення
fmExample.edInput.SetFocus
end else / / – Ні: знову пускаємо таймер
uEventID := timeSetEvent(Delay,0,@ProcTime,0,1);
end;

procedure TfmExample.bbRunClick(Sender: TObject);
/ / Запускає таймер. edInput містить необхідний період.
begin
/ / Перевіряємо завдання періоду
if edInput.Text= then Exit;
try
Delay := StrToInt(edInput.Text)
except
ShowMessage (Помилка введення числа);
edInput.SelectAll;
edInput.SetFocus;
Exit
end;
Counter: = 0; / / Скидаємо лічильник
BegTime: = Time; / / Засікаємо час
/ / Запускаємо таймер:
uEventID := timeSetEvent(Delay,0,@ProcTime,0,1);
if uEventID=0 then
ShowMessage (Помилка запуску таймера)
end;

procedure TfmExample.FormActivate(Sender: TObject);
begin
edInput.SetFocus
end;

end.


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*