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

В модуле Windows эта функция объявлена следующим образом:

function CreateProcess(lpApplicationName: PChar;
lpCommandLine: PChar;
lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
blnheritHandles: BOOL;
dwCreationFlags: DWORD;
IpEnvironment: Pointer;
lpCurrentDirectory: PChar;
const IpStartupInfo: TStartupInfо;

var lpProcessInformation: TProcessInformation): BOOL; Параметры описаны ниже.

• lpApplicationName - имя исполняемого файла с возможным маршрутом доступа. Если маршрут доступа опущен, имя дополняется текущим диском и текущим каталогом. Если параметр имеет значение NIL, имя исполняемого файла должно указываться первым в перечне параметров командной строки lpCommandLine и отделяться от других параметров пробелом. Если запускается 16-разрядная программа под управлением Windows NT/ 2000/ХР, параметр lpApplicationName должен быть равен NIL.

• lpCommandLine - строка командных параметров. Если параметр имеет значение NIL, командные параметры можно передавать программе в параметре lpApplicationName. Если запускается 16-разрядная программа под управлением Windows NT/2000/XP, первым в списке параметров должно стоять имя исполняемого файла с возможным маршрутом доступа.

• lpProcessAttributes - указатель на структуру, описывающую привилегии доступа процесса. Имеет смысл только для Windows NT/2000/XP. Если имеет значение NIL, процессу присваиваются привилегии доступа, заданные по умолчанию.

• lpThreadAttributes -~ указатель на структуру, описывающую привилегии доступа главного потока команд программы. Имеет смысл только для Windows NT/2000/XP. Если имеет значение NIL, потоку присваиваются привилегии доступа, заданные по умолчанию.

• blnheritHandles - указывает, будет ли новый процесс наследовать дескрипторы вызывающего процесса. Если имеет значение True, порождаемый процесс разделяет память, карту файлов и другие системные атрибуты со своим родителем.

• dwCreationFlags - один или несколько флагов, уточняющих способ создания нового процесса и его приоритет:

* CREATE_DEFAULT_ERROR_MODE - новый процесс получает свой обработчик ошибок;
* CREATE_NEW_CONSOLE - новый процесс получает свои консольные средства ввода-вывода (не совместим с флагом DETACHED_PROCESS);
* CREATE_NEW_PROCESS_GROUP - новый процесс будет корневым для группы процессов;
* CREATE_SEPARATE_WOW_VDM - новый 16-разрядный процесс Windows будет запущен на отдельной виртуальной машине DOS (имеет смысл только для Windows NT/2000/XP);
* CREATE_SHARED_WOW_VDM - проверяет параметр Def aultSeparateVDM в файле Win.ini и, если он имеет значение True, запускает новый 16-разрядный процесс на отдельной виртуальной машине DOS, в противном случае - на общей виртуальной машине (имеет смысл только для Windows NT/2000/XP);
* CREATE_SUSPENDED - после создания нового процесса его главный поток команд не будет активным (его можно впоследствии активизировать обращением к функции ResumeThread);
* CREATE_UN I СО DE_ENV IRONMENT - если установлен, блок заканчивающихся нулем строк Environment имеет кодировку Unicode;
* DEBUG_PROCESS - новый процесс будет отлаживаться вызывающим процессом;
* DEBUG_ONLY_THIS_PROCESS - если не установлен, каждый процесс, порождаемый новым отлаживаемым процессом, становится еще одним отлаживаемым процессом вызывающего процесса;
* DETACHED_PROCE S S - если установлен, новый консольный процесс не имеет собственных консольных средств ввода-вывода, но может их запросить обращением к функции AllocConsole (не совместим с флагом CREATEJSIEW_CONSOLE);
* HIGH_PRIORITY_CLASS - присваивает главному потоку команд нового процесса высокий приоритет;
* IDLE_PRIORITY_CLASS - главный поток нового процесса будет активизироваться только тогда, когда нет ни одного активного потока;
* NORMAL_PRI ORI TY__CLAS S - присваивает обычный приоритет;

* REALTIME_PRIORITY_CLASS - присваивает наивысший приоритет.

• IpEnvironment - указатель на блок оканчивающихся нулем строк, описывающих переменные окружения нового процесса (например, умалчиваемые каталоги поиска). Каждая переменная задается строкой вида параметр = значение. Если содержит NIL, процесс использует значения переменных окружения, заданные по умолчанию.

• lpCurrentDirectory - указатель на оканчивающуюся нулем строку, устанавливающую для нового процесса диск и каталог, заданные по умолчанию.

• IpStartupInf о - указатель на структуру, описывающую положение и размеры главного окна нового процесса, его средства ввода-вывода и т. п.

• IpProcessInformation - указатель на структуру, создаваемую функцией CreateProcess, в которой содержится информация о вновь созданном процессе и его главном потоке команд.

Если вызов был успешным, функция возвращает значение True, если неудачным, причину неудачи может прояснить код, возвращаемый функцией GetLastError. Структура StartUpInf о такова:

type TStartUpInfo = record

cb: DWORD; // Длина структуры в байтах

lpReserved: Pointer; // Всегда NIL

lpDesktop: Pointer;

//

Обычно NIL

lpTitle: Pointer;

//

Обычно NIL

dwX: DWORD;

//

Положение окна

dwY: DWORD;
dwXSize: DWORD;

//

Его размеры

dwYSize: DWORD;
dwXCountChars: DWORD;

//

Обычно NIL

dwYCountChars: DWORD;
dwFillAttribute: DWORD;

//

Обычно NIL

dwFlags: DWORD;

//

Управляющие флаги

wShowWindow: Word;

//

Состояние окна при старте

cbReserved2: Word;

//

Всегда 0

lpReserved2: PByte;

//

Всегда NIL

hStdlnput: THandle;
hStdOutput: THandle;
hStdError: THandle;
end;

Многие поля этой структуры в обычных случаях должны быть нулевыми. В обычных программах, использующих графический интерфейс Windows, реально необходимы только поля cb, dwFlags и wShowWindow. Первое должно содержать длину структуры, второе - управляющие флаги, третье - определять состояние окна в момент первого появления его на экране. Если желательно управлять размерами окна в момент старта (развернутое, свернутое, нормальное и т. п.), поле dwFlags должно содержать значение STARTFJJSESHOWINDOW, что заставит систему учитывать значение поля wShowWindows. Значениями могут быть константы SW_XXXX, перечисленные в табл. 15.2.

Таблица 15.2. Константы SW.XXXX

Константа

Назначение

5\ЛМНЮЕ

Скрывает текущее окно. Активным становится другое окно

5\Л/_МАХ1МЕЕ

Отображает окно полностью развернутым

5\Л/_М1М1М1гЕ

Сворачивает окно

SW_RESTORE

Восстанавливает размеры окна, которые были до свертывания

или развертывания

SW_SHOW

Отображает окно с текущими размером и позицией

SW_SHOWDEFAULT

Отображает окно в виде, который определен структурой 51агЦ|р1пй)

БУУ.БНОУУМАХГМЕЕО

Активизирует или отображает развернутое окно

Б\/У_5Н0\Л/М1М1М1гЕ0

Активизирует или отображает свернутое окно

БУУ.ЗНОУУМШМОАСПУЕ

Отображает окно свернутым и неактивным

Отображает окно в его текущем состоянии и неактивным

БУ^БНОМОАСПУЕ

Отображает окно в том виде, в котором оно отображалось последний

раз, и неактивным

SW_SHOWNORMAL

Отображает окно в том виде, в каком оно отображалось последний раз

Структура Processlnf о содержит информацию о процессе:

TProcessInformation = record

hProcess: THandle; // Дескриптор процесса

hThread: THandle; // Дескриптор потока

dwProcessId: DWORD; // Идентификатор процесса

dwThreadld: DWORD; // Идентификатор потока end;

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

funtcion WaitForSingleObject(hHandle: Handle;
dwMS: DWORD): DWORD;

Функция выжидает dwMS миллисекунд и возвращает информацию о текущем состоянии процесса, определенного его дескриптором hHandle:

• WAI Т_АВAN DONE D - процесс заблокирован;
• WAIT_OBJECT_0 - процесс завершился;

• WAIT_TIMEOUT - процесс не завершился.

Таким образом, компонент сначала создает процесс и запускает внутренний таймер. В обработчике таймера он проверяет текущее состояние процесса и возбуждает событие Af terExecute, если запущенный процесс закончился.

Компонент должен быть специальной кнопкой, чтобы щелчок на ней мог запустить приложение (процесс). Так как компонент будет использовать значок запускаемого приложения, он должен иметь средства отображения значка. Такие средства имеет компонентный класс TSpeedButton. Модуль компонента представлен в листинге 15.6.

Листинг 15.6. Модуль компонента FarRunButton unit FarRunButton;

Этот модуль содержит компонент, позволяющий запустить внешнюю программу и проконтролировать ее завершение. Свойство AppName содержит имя исполняемого файла и, возможно, маршрут доступа к нему. Свойство CommandLine содержит командную строку, передаваемую запускаемому приложению. Свойство ShowWindow определяет вид окна стартующей программы. Свойство ViewAfterRun содержит значение перечисления, определяющего вид кнопки после успешного старта программы: агHide - компонент исчезает после старта программы и появляется вновь после окончания ее работы; arDisabled - компонент запрещен к выбору в момент работы программы; arShow - компонент не меняет свой вид. Свойство Interval определяет период (в миллисекундах) проверки завершения работы программы. Событие AfterExecute возбуждается после окончания работы программы. Его обработчик получает код завершения программы.

продолжение &

Листинг 15.6 (продолжение) interface

uses

SysUtils, Classes, Controls, Buttons, Graphics, ExtCtrls, Windows;
type TAppName = type String;
TAfterExecute = procedure(Sender: TObject;
Result: DWORD) of object;
TShowWindow - (SW_HIDE, SW_NORMAL, SW_SHOWMINIMIZED, SW_MAXIMIZE, SW_SHOWNOACTIVATE, SW_SHOW, SW_MINIMIZE, SW_SHOWNOACTIVE, SW_SH0WNA, SW_RESTORE, SW_DEFAULT);

// Следующее перечисление определяет вид кнопки после

// успешного запуска:

TViewAfterRun = (arShow, arHide, arDisabled); TFarRunButton = class(TSpeedButton) private { Следующие поля нужны для того, чтобы убрать ненужные свойства из окна Инспектора объектов } FCaption: TCaption;
FAllowAllUp: Boolean;
FFont: TFont;
FGroupIndex: Integer;
FLayOut: TButtonLayOut;
FOnClick: TNotifyEvent;

// Следующие поля нужны для вновь вводимых свойств FTimer: TTimer; FInterval: Integer; FAppName: TAppName; FCommandLine: String; FViewAfterRun: TViewAfterRun; FAfterExecute: TAfterExecute; FShowWindow: TShowWindow; protected procedure SetAppName(Value: TAppName); function IsExeFile(Value: TAppName): Boolean; procedure Click; override; procedure ProcessRun; procedure OnTimer(Sender: TObject); public

{ Public declarations } constructor Create(AOwner: TComponent); override; // Удаляемые свойства

property Caption: TCaption read FCaption;
property AllowAllUp: Boolean read FAllowAllUp;

property Font: TFont read FFont; property Grouplndex: Integer read FGroupIndex; property LayOut: TButtonLayOut read FLayOut; property OnClick read FOnC lie Republished

property Enabled default False; // Меняем

// умалчиваемое значение

{ Новые свойства } property AppName: TAppName read FAppName

write SetAppName; property CommandLine: String read FCommandLine

write FCommandLine; property ViewAfterRun: TViewAfterRun read FViewAfterRun

write FViewAfterRun default arDisabled; property Interval: Integer read FInterval

write FInterval default 1000; property AfterExecute: TAfterExecute read FAfterExecute

write FAfterExecute; property ShowWindow: TShowWindow read FShowWindow

write FShowWindow default SW_NORMAL;
end;
procedure Registers-implementation uses ShellAPI;
procedure Register;
begin
RegisterComponents(1 Far1, [TFarRunButton]);
end;
/ TFarRunButton }
constructor TFarRunButton.Create(AOwner: TComponent);

// Вызывает унаследованный конструктор, затем

// создает встроенный таймер и устанавливает

// умалчиваемые значения свойств

begin
inherited;
FTimer := TTimer.Create(Self);
FTimer.Enabled i~ Falser-Interval := 1000; ViewAfterRun := arDisabled;
ShowWindow := SW_NORMAL;
Height := 40;

продолжение

Листинг 15.6 (продолжение)
Width 40;
Enabled := False // Кнопка неактивна, так как нет программы end;
procedure TFarRunButton.SetAppName(Value: TAppName); // Контролирует существование файла и его расширение. // Если файл существует и исполняемый, копирует значок // приложения в свое свойство Glyph. var
Icon: TIcon;
begin
if FileExists(Value) and IsExeFile(Value)
and (FAppName <>
Value) then
begin
FAppName := Value;

Enabled := True; // Кнопка доступна, // так как связана с программой Icon := TIcon.Create; try

// Извлекаем значок программы:

Icon.Handle := Extractlcon(hlnstance, PChar(Value), 0); with Glyph, Canvas do begin // Помещаем значок в свойство Glyph кнопки Canvas.Brush.Style := bsSolid; Canvas.FillRect(Canvas.ClipRect); Width := Icon.Width; Height := Icon.Height; Canvas.Draw(0, 0, Icon); end finally

Icon.Free end end end;
function TFarRunButton.IsExeFile(Value: TAppName): Boolean;

// Проверяет расширение файла

var
Ext: String;
begin
Ext := ExtractFileExt(Value);
Result := Uppercase(Ext) = '.EXE' end;
procedure TFarRunButton.Click;

// Перекрывает унаследованный метод, // чтобы запустить программу

begin
inherited;
ProcessRun end;

var Processlnfo: TProcessInformation; { Эта структура сделана глобальной, так как используется в методе ProcessRun и в обработчике таймера } procedure TFarRunButton.ProcessRun; // Запускает программу и таймер var StartUpInfo: TStartUpInfо; Result: LongBool; begin // Готовим структуру StartUpInfo:

FillChar(StartUpInfo, SizeOf(TStartUpInfo), #0);
with StartUpInfo do
begin
cb := SizeOf(TStartUpInfo);
dwFlags :- STARTF_USESHOWWINDOW;
wShowWindow := ord(FShowWindow) end;

// Запускаем программу

Result := CreateProcess(NIL, PChar(AppName + 1 '

+ CommandLine), NIL, NIL, False, NORMAL_PRI0RITY_CLASS, NIL, NIL, StartUpInfo, Processlnfo); if not Result then // Исключение - программа не запущена

raise Exception.Create('Ошибка '

+ IntToStr(GetLastError) + ' при старте программы 1 + AppName) else begin // Нормальный запуск

case ViewAfterRun of

arHide: Hide; // Прячем кнопку

arDisabled: Enabled :- False; // Запрещаем выбор

end;
FTimer.OnTimer := OnTimer;
if Interval <= 0 then
FInterval := 1000; FTimer.Interval := Interval;
FTimer.Enabled := True; // Пускаем таймер end;
end;
procedure TFarRunButton.OnTimer(Sender: TObject); // Проверяет завершение программы продолжение &

Листинг 15.6 (продолжение) var Result: DWORD; begin if WaitForSingleObject(Processlnfо.hProcess, 1) =

WAIT_OBJECT_0 then begin // Программа завершилась

FTimer.Enabled := False; // Остановка таймера

// Получаем код завершения программы:

GetExitCodeProcess(Processlnfo.hProcess, Result);

// Освобождаем дескрипторы:

CloseHandle(Processlnfo.hThread);
CloseHandle(Processlnfo.hProcess);

case FViewAfterRun of

arHide: Show; // Показываем кнопку

arDisabled: Enabled := True; // Делаем ее доступной

end;
// Возбуждаем событие AfterExecute: if Assigned(FAfterExecute) then FAfterExecute(Self, Result)
end end;

end.

Обратите внимание: структура Processlnfo используется при запуске программы в методе ProcessRun и при проверке завершения ее работы в обработчике таймера, поэтому она сделана глобальной.

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

Result := CreateProcess() ;

if Result then with Processlnfo do begin WaitForlnputldle(hProcess, INFINITE); // Ждем начала работы CloseHandle(hThread); // Освобождаем дескрипторы

CloseHandle(hProcess) end;

Некоторые свойства, унаследованные от родительского класса TSpeedButton (Caption, AllowUp, Font и т. п.), нашему компоненту не нужны. Как уже говорилось, дочерний класс не может уменьшить степень видимости членов родительского класса, но он может убрать их из окна Инспектора объектов, если объявит их доступными только для чтения и (или) разместит их в секции public. Именно таким образом заново объявляются ненужные свойства в нашем компоненте.

15.4. Компонент FarRunButton || Оглавление || 15.4.3. Редактор свойства AppName


Искусство создания компонентов Delphi



Новости за месяц

  • Июль
    2020
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс