Главная » Delphi » Простая программа в виде иконки — отладочный пример

0

Попробуем отвлечься, наконец, от изрядно надоевшего SlideShow и создать программу, которая всегда находится в виде иконки: в Тгау и лишь по некоторым событиям производит какие-то определенные действия. Главного окна-формы такая программа может и не иметь.

Об окнах it сообщениях

Любая графическая программа в Windows имеет по крайней мере одно окно. Если вы создавали стандартную программу Delphi, то это окно главной формы. Дескриптор окна имеет тип hwnd. Но окно может и не быть связано с конкретной формой, т. е. не отображаться на экране, и все-таки оно есть. Именно через окно происходит взаимодействие программы с другими программами. Для этого с любым (даже невидимым) окном связана так называемая оконная процедура— внутренний цикл обработки системных сообщений.

Каждая программа в Windows имеет свой главный дескриптор (application handle), указывающий на область памяти, в которой эта программа расположена. Этот дескриптор имеет такой же тип, как и у окна программы (hwnd), и в текстах программ часто называется hinstance (Instance — пример, экземпляр), чтобы подчеркнуть факт, что этот дескриптор свой для каждого конкретного экземпляра запущенной программы. Напомню (см. главу 2), что дескриптор (handle)— вообще-то просто число, поэтому во многих случаях любые дескрипторы совместимы с типом integer или longint.

Приложения, у которых графическое окно отсутствует, но, тем не менее, они могут вести диалог с пользователем, называются консольными (от слова console — устройство ввода-вывода), т. к. взаимодействуют с клавиатурой и экраном напрямую, без посредства визуальных компонентов. Так как консольные приложения графического окна не имеют связанной с ним оконной процедуры, то сами по себе они исключены из потока системных сообщений Windows. При необходимости что-то вывести на экран для консольного приложения открывается текстовое окно, подобное окну DOS-программы. И само создание консольных приложений напоминает написание DOS- или

Windows-программ в невизуальной среде, например, для ввода и вывода используются знакомые по Turbo Pascal функции read, write, readin, writein. Консольные программы проще по строению и не занимают столько ресурсов, сколько графические программы. Примеры консольных приложений вы могли встречать не раз — это всем известный файловый менеджер Far, многие стандартные системные утилиты, или, например, клиентские программы на рабочих местах операционисток на почтах, в банках или обменных пунктах валюты. Для того чтобы создать консольное приложение, можно обратиться к пункту File | New | Other и затем выбрать из многочисленных предлагаемых вариантов Console Application. В этой книге консольные приложения вам почти не встретятся, но запомните, что так, например, удобно создавать одноразовые утилитки для каких-то расчетов без необходимости обращаться к Turbo Pascal.

О сообщениях Windows. Обработчики событий, имеющиеся у каждого потомка TWinControl, обрабатывают примерно 20% типов сообщений, которые могут поступать в главную оконную процедуру, что соответствует примерно 80% процентам жизненных ситуаций. Для перехвата остальных сообщений нужно либо переопределить метод WndProc (мы это делали в главе J), либо перекрыть его. В первом случае оригинальный обработчик вызывается автоматически. Перекрытие осуществляется таким, например, образом:

TForml = class (TForm)

procedure FormDblClick(Sender: TObject);

private

procedure WMLButtonDblClick(var Msg:TWMLButtonDblClk);

message WM_LBUTTONDBLCLK ;

end;

procedure TForml.WMIButtonDblClickfvar Msg:TWMLButtonDblClk); begin

ShowMessage(Format(‘Click at %d,%d’,[Msg.XPos,Msg.YPos])); end;

procedure TForml.FormDblClick(Sender: TObj ect) ; begin

ShowMessage(‘Right Dbl Click’) ; end;

Здесь мы определяем свой обработчик для события двойного щелчка левой кнопкой. Само имя процедуры обычно делают совпадающим с наименованием перехватываемого сообщения (WMLButtonDblClick), однако это совершенно необязательно, имя может быть любым удобным, а вот идентификатор самого сообщения должен совпадать с определенной в Windows или ранее объявленной (для собственного сообщения, см. далее) константой. Параллельно я тут показал обычный обработчик двойного щелчка на форме (FormDblciick), который создается средствами Delphi. Приведенный код будет делать следующее: при двойном щелчке левой кнопкой мыши событие будет поступать в наш обработчик, а правой — в стандартный. Чтобы вызвать стандартный обработчик после нашего (или перед ним), нужно добавить в созданную процедуру слово inherited (соответственно, в конце или в начале ее).

Можно также определить в системе и свое собственное сообщение, для чего нужно объявить сначала свою собственную константу, обычно это делается через базовую константу wm User, например:

const wm_MyMessage= wm_User+l;

Послать такое сообщение можно через функцию SendMessage, например, вот так:

SendMessage (SorneForm.Handle, wm_KyMessagfi, 1,0);

"Поймать" такое сообщение в форме-адресате можно точно такой же функцией перехвата, как была сделана ранее для двойного щелчка, только объявив, разумеется, точно такую же константу. Бояться, что номер, объявленный через wm User, случайно совпадет с объявленным в другой программе, не нужно, если сообщение действует в пределах одного приложения (константа тогда действует подобно локальным переменным). В главе 7 мы покажем, как в иных случаях образовывать уникальный номер сообщения с помощью генератора случайных чисел.

Кроме этого, сообщения можно перехватывать через обработчик Application.onMessage [3]. Мы будем пользоваться всеми этими способами в дальнейшем. У каждого сообщения есть параметры wPaxam и lPapsm, которые имеют разные значения в каждом отдельном случае. Подробное описание структуры MSG с этими параметрами, функции SendMessage и других функций, связанных с сообщениями, см. в [16,18].

Здесь мы создадим новый проект (File | New | Application), а затем уберем из него заготовку формы unitl (и, соответственно, одноименного модуля) через меню View | Project Manager (выделить в пункте Projectl подпункт Unitl и нажать Remove). После этого у вас все, относящееся к проекту, исчезнет вообще. но не пугайтесь: надо зайти опять в меню View, найти там пункт Units и выделить единственный оставшийся пункт Projectl. Тогда у вас появится заготовка самой простой Delphi-программы (файл Projectl.dpr). Сохраним проект под этим же именем (на диске он находится в папке Glava5\2).

На этом примере я хочу вам продемонстрировать процесс создания программы "по образцу". Это очень удобный и часто встречающийся прием — зачем изобретать велосипед и отлаживать то, что уже отлажено? Если бы вы создавали крупное коммерческое приложение, предназначенное для продаж по всему миру, вас могли бы осудить за нарушение права собственности, но и то только лишь в том случае, если бы вы использовали проприетарные участки кода. Нормальные программисты часто сами публикуют исходный код своих программ для повторения и модернизации — или просто так, или, как в случае "свободного софта" (распространяемого по лицензии GPL), с условием доступности исходного кода и упоминания авторов. Независимо от лицензии, упоминать источник есть просто дань вежливости, и мы именно так и поступим. Мы возьмем подходящую готовую и заведомо работающую программу из главы 1 книги [5], добавим нужную и уберем лишнюю функциональность.

А как зафиксировать имя автора исходного образца так, чтобы упоминание о нем сохранилось в программе? Обычный способ — включить его в меню About— нам здесь не подходит, т. к. программа простейшая и загромождай, ее всякими меню нет никакого смысла. Можно использовать окно-заставку, но и это уже будет не простейшая программа — от использования одного только BMP-файла размер программы увеличится раз в пятнадцать. Однако Delphi предоставляет способ включения в исполняемый файл произвольной текстовой информации (до 255 символов). Если вы просмотрите любой "фирменный" ЕХЕ-файл в текстовом режиме (такую возможность предоставляют, например, просмотрщики, встроенные в некоторые из клонов Norton Commander), вы обязательно найдете подобную информацию о фирмо- производителе, пакете программирования или фамилиях разработчиков. Этим всегда пользуются настоящие (без кавычек) программисты — был даже забавный скандал, когда Microsoft уличили в поддержке пиратства, обнаружив в некоторых файлах из комплекта Windows незамеченный ее программистами текст, свидетельствующий об использовании ими "крякнутого" (crack) софта. Альтернативный штатный способ включения информации в исполняемый файл предоставляют ноля структуры version.info, хранящиеся в ресурсах исполняемого файла. При наличии формы для заполнения этих полей мы пользуемся соответствующим пунктом в меню Project, а способ ввести эту структуру вручную мы будем рассматривать в главе 11.

Итак, для того чтобы включить в нашу программу текст с именем автора, найдем в меню Project | Options закладку Linker, а там пункт ЕХЕ Description, и впишем (естественно, по-английски) в поле этого пункта что- нибудь вроде: "Programmer Revich Y. Based on template by Flenov M.V." (Программист Ревич Ю. Основано на шаблоне Фленова М. В.). Не забудьте после компиляции заглянуть в ЕХЕ-файл — в конце его вы найдете этот текст в кодировке Unicode, т. е. каждый символ текста будет предваряться байтом со значением 0, означающим английский язык (о кодировках мы еще поговорим в главе 5).

Сейчас мы на основе этой заготовки создадим прототип программы — приложение с пустым окном (представляющим собой просто серый прямоугольник), воспользовавшись образцом, но сразу модернизировав его таким образом, чтобы сначала создавалась иконка в Tray, а уже через нее происходили всякие нужные нам действия. Для того чтобы проще было отлаживать программу, мы будем отступать от образца постепенно: в первом варианте пусть по щелчку на созданной иконке показывается это самое пустое окно, уничтожающееся по нажатию клавиши <Esc> (последнее так, как в образце — процедура обработки соответствующего сообщения там уже отработана).

После этого заменим весь текст в заготовке программы Projectl .dpr после заголовка (не забыв сохранить указание компилятору ($r *.res}) на следующий (я сразу модернизировал текст образца, сократив ненужные структуры и вставив процедуру создания иконки) — листинг 5.1.

| Листинг 5.1. Заготовка программы Projectl .dpr

program Projectl;

uses

windows, Messages, ShellApi, SysLJtils;

{$R *.RESl

var

Instance: HWnd; {дескриптор модуля приложенияi WindowClass: TWndClass; {класс окна приложенияI FHandle: HWnd; {дескриптор окна приложения} mesg: TMsg; /сообщения приложения} Ico_Maeeage: integer=wm_User; {сообщение от иконки окну} noIconData: TNotifylconData; {дескриптор структуры

NotifylconData) Hlconl: hlcon; {дескриптор иконки!

function WindowProc (Hwn,mag,wpr,lpr: longint): longint; atdcall; {обработка сообщенийI begin

result.-=defwindowproc(hwn,msg,wpr,lpr);

{ищет стандартный обработчикI if Msg = wm_Destroy than {если команда на закрытие} begin

Shell_NotifyIcon(NIM_Delete, @no!conData); {удаляем иконку/

halt; (закрываем программу I end;

if msg=wm_KeyDown then

if wpr=VK_ESCAPE then (при нажатии Esc удаляем программ,у} begin

SheLl_NotifyIcon(NIM_Delete,@nolconData); (удаляем иконку) halt; end;

if Msg = lco_Massage then (если это сообщение от иконки) begin

if lpr=WM_LBUTTONUP then (была отпущена левая кнопка} begin

ShowWindow(FHandle, SW_SHOW); (демонстрируем окно) UpdateWindow (FHandle); end; end; end;

procedure CreateMyicon; begin

Hlconl:=LoadIcon(Instance,’MAINICON’); (получаем дескриптор

иконки) with noIconData do begin

cbSize:=Sizeof(TNotifylconData); (размер структуры) Wnd:=FHandle; (дескриптор окна) uID:=0; (единственная иконка)

UFlags:=NIF_MESSAGE or N1F_IC0N or N1F_TIP; (взводим все флаги) SzTip:=’Projectl'; (всплывающая подсказка) HIcon:=HIconl; (дескриптор иконки/

uCallBackMessage:=Ico_Massage;(определяемое пользователем сообщение}

end;

Shell_NotifyIcon(N1M_ADD,SnoIconData); (создали иконку/ end;

begin

instance :=>GetModuleHandle(nil); (получаем дескриптор модуля/

with WindowClass do {задаем свойства окна)

begin

style:=CS_HRedraw or CS_VRedraw; LpEnwndproc:=@windowproc; Hinsta nee:=Ins tance; HbrBackground:= color_btnface;

Lps zCla ssName: =’DX'; Hcursor:=LoadCursor(0,IDC_ARR0W); hlcon:=LoadIcori (Instance,’MAINICON’); end;

RegisterClass (WindowClass); Iрегистрируем класс)

FHandle:=CreateWindowEx (0,’DX’, ",WS_POPUP, 5,5, 200, 200, 0,0,instance, nil); (получаем дескриптор окна)

CreateMyicon; (создаем иконку)

while (GetMessage(mesg, 0, 0, 0)) do (цикл обработки сообщений) begin

TranslateMessage(mesg); DispatchMessage (mesg); end; end.

Здесь процедура обработки сообщения wm Destroy нужна для того, чтобы при выключении компьютера программа "еамоуничтожалась" — иначе выход из Windows при запущенной программе может неоправданно затянуться с получением различных грозных сообщений.

Запустим приложение из среды Delphi и убедимся, что все работает — при запуске создается иконка, при щелчке на ней левой кнопкой в верхнем левом углу экрана возникает пустое окно-прямоугольник 200×200, при нажатии клавиши <Esc> программа закрывается, и иконка при этом уничтожается. Если мы исследуем свойства полученной программы немного подробнее, то увидим, что при запуске вне среды Delphi программа не будет реагировать на нажатие <Esc>, пока мы не сфокусируемся на ее пустом окне мышью. Это и понятно — пока окно не сфокусировано, оно не знает, что нажатие клавиши <Esc> предназначено именно ему. Можно исправить это, заставив окно при вызове сразу располагаться поверх всех окон (вызовом функции BringWindowToTop), но это будет полумера для данного конкретного случая: нам окно вообще-то само по себе не требуется, наша задача сделать так, чтобы программа работала по нажатию клавиши вообще без всякого (видимого) окна. Поэтому мы воспользуемся регистрацией горячей клавиши, как делали раньше. Вставим вызов регистрационной функции в нашу программу перед созданием иконки (клавишу возьмем ту же самую <Ctrl>+<F12>):

RegisterHotKey(FHandle, 1 ,MDD_CONTROL, vk_F12); (регистрируем

клавишу Ctrl+F12) CreateMyicon; (создаем иконку)

А в процедуру обработки сообщений добавим следующий текст:

if Msg = WM_HOTKEY then {если нажата горячая клавиша}

if wpr=l then ее номер 1}

begin

Shell_NotifyIcon(NIM_Delete,@noIconData); (удаляем иконку} UnregisterHotKey(FHandle, 1); (убираем горячую клавишу} halt; (закрываем программу} end;

Запустим программу и убедимся, что она работает как надо: при запуске иконка создается, а при нажатии комбинации клавиш <Ctrl>+<F12> программа закрывается. Теперь отладка прототипа закончена. Кстати, обратите внимание на размер исполняемого файла— чуть больше 40 Кбайт. Вот что значит не использовать графические ресурсы!

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии