Главная » Delphi » Предотвращение повторного запуска приложения

0

Повторный запуск приложения иногда может наделать много неприятностей— например, если вы работаете с одним конкретным файлом или с одной конкретной базой данных. Позже, когда мы будем говорить о различных способах доступа к файлам из приложения, мы приведем пример такого случая. Чаще же всего повторный запуск просто раздражает, отнимая время и ресурсы компьютера. Разумеется, есть случаи — и их достаточно много — когда возможность повторного запуска предусмотрена специально (так, вы можете запустить сразу несколько экземпляров Delphi, или несколько Word), а иногда и жизненно необходима— скажем, для Internet Explorer работа с несколькими ресурсами параллельно производится только через запуск все новых и новых его экземпляров. Но во всех случаях, когда запуск второго, третьего и т. д. экземпляров программы возможен, это должно быть осмысленным решением, а не наоборот— когда предотвращением повторного запуска занимаются только в самых критичных случаях.

Для того чтобы предотвратить повторный запуск, проще всего в самом начале загрузки программы определять, какие приложения уже запущены. Если в списке встретится наше приложение, мы делаем вывод, что оно уже было запущено и предпринимаем какие-либо действия — выводим сообщение или просто прерываем загрузку. Тут сразу возникает несколько вопросов. Как составить список запущенных приложений? Что конкретно означают слова "встретится наше приложение", как его идентифицировать? И, наконец, где именно располагать нашу процедуру? Ответим на них в порядке посту пления.

Список запущенных приложений можно получить с помощью функции Getwindow1. Она имеет два параметра: дескриптор окна, от имени которого производится обращение (знакомого нам типа hWnd) — т. е. в данном случае это дескриптор нашего приложения, и параметр (типа uCmd), указывающий на отношения между нашим приложением и тем, которое мы ищем. Нас будут интересовать три возможных значения этого параметра: gw hwndfirst — идентифицирует окно того же самого типа, что и наше, которое является самым верхним в списке запущенных приложений, gw hwndnext — следующее в списке окно, и gw owner— идентифицирует дочерние окна, которые нас не интересуют. Возвращает функция Getwindow дескриптор найденного окна.

Идентифицировать приложение мы будем по его имени. Это не то же самое, что имя файла приложения или заголовок окна формы. Хотя имя приложения по умолчанию будет соответствовать имени файла, но оставлять это в таком виде неправильно (файл ведь можно и переименовать). Специально установить имя можно еще на стадии проектирования (через пункт Project | Options | Application, там же, где устанавливается иконка приложения). Если вы установите имя "SlideShow" через указанный пункт меню, то увидите, что в тексте основной программы (файл SlideShow.dpr) автоматически появится строка:

Application.Title ‘SlideShow';

А получить имя приложения (оно же название главного окна), зная дескриптор его окна, можно через функцию GetWindowText. Имейте в виду, что возвращаемое название чувствительно к регистру букв — "SlideShow" и "Slideshow" будут идентифицировать совершенно разные приложения!

Наконец, разберем вопрос о том, где располагать нашу процедуру. Очевидно, что располагать ее в тексте модуля slide.pas (по какому-либо событию) невыгодно — тогда, чтобы ее выполнить, нам надо по крайней мере начать создание формы, а ради чего все затевалось? Поэтому нам придется вмешаться в основную программу.

Заметки на полях

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

Текст, который нужно выполнить, мы вставим в основную программу до того, как начнется выполнение процедуры создания формы, т. е. перед строкой Application.CreateForm(TForml, Formi). Как всегда, скопируем последний вариант проекта SlideShow из папки Glava3\3 в новую папку (на диске это папка с именем "1" внутри папки GIava4) и изменим номер версии (пусть это будет 1.13). Кстати, из изложенного ранее следует, что если вы хотите, чтобы версии воспринимались при запуске, как разные программы, нужно внести изменения и в название приложения (и, желательно, также сменить иконку) — однако здесь все версии делают одно и то же, поэтому так поступать мы не будем. Модифицированная программа (файл SlideShow.dpr) у нас будет выглядеть так (обратите внимание, что нам пришлось вставить ссылку на модуль Windows в предложение uses) — листинг 4.1.

Листинг 4.1. Модифицированная программа SlideShow.dpr

program SlideShow; uses

Forms, Windows,

slide in ‘slide.pas’ {Formi};

{$R *.res I

var pbuff:array[0..127] of char; {буфер для текста J

var dWin: HWnd; {дескриптор)

var st:string; (вспомогательная строка)

begin

Application.Initialize; Application.Title := ‘SlideShow';

dWin:= GetWindow(Application.Handle, GW_HWNDFIRST);

while dWin <> 0 do

begin

if (dWin <> Application.Handle) and {собственное окно игнорируем) (GetWindow(dWin, GW_OWNER) = 0! and

{дочерние окна игнорируем} (GetWindowText(dWin, pbuff, sizeof(pbuff)) <> 0) (без названия игнорируем)

then begin

GetWindowText(dWin, pbuff, sizeof(pbuff));

{получаем текст названия приложения} st:-string(pbuff); {переводим его в строку) if st=’SlideShow* then begin

st:=’"Двое пернатых в одной клетке не живут." ЭА.ЛеСедь1; Application.MessageBox(Pchar(st),”,MB_OK);

{выводим предупреждение) exit; {прерываем программу) end; end;

dWin:= GetWindow(dWin, GW_HWNDNEXT);

{ищем следующее приложение из списка)

end;

Application.CreateForm(TForml, Forml);

Application.Run;

end.

При попытке повторного запуска программа найдет в списке запушенных приложений свое имя и выдаст сообщение, представляющее собой знаменитое высказывание покойного генерала. После нажатия Ok запуск программы будет прерван. Вывод сообщения ‘"Двое пернатых в одной клетке не живут." @а.Лебедь’ здесь сделан только для иллюстрации: разумеется, никаких сообщений можно и не выдавать, а сразу выполнить команду exit; с другой стороны, можно просто организовать выбор, запускать второй экземпляр программы или нет.

Delphi не позволит вам запустить программу из своей среды повторно. Для того чтобы проверить работу программы в этом режиме, надо произвести компиляцию, полученный исполняемый файл SlideShow.exe скопировать в любую другую папку, запустить его оттуда, а затем попробовать еще раз запустить приложение из среды Delphi. Отметим также, что использование сразу двух видов представления одной и той же строковой переменной (как массив символов pbuff и как паскалевская строка st) не является необходимым — можно было бы и сократить программу в этом отношении, просто работа с паскапевскими строками нагляднее.

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

ShowWindow(dWin, SW_SHOWNO№AL) ; BringWindowToTop(dWin);

По вызову этих функций первый экземпляр приложения будет максимизирован (если он был свернут) и — по идее — выведен вперед всех окон, а попытка запуска второго будет пресечена последующим оператором exit (по этому образцу работает, например, TheBat!). Аналогичную функциональность предоставляют функции SetWindowFos, SetForegroundWindow И др. (см. справку по Win32). Здесь функция BringwindowToTop добавлена для надежности — в принципе и showWindow и другие указанные функции в одиночку должны активировать окно, но механизм вывода приложений в активное состояние в Windows настолько запутан, что в некоторых случаях и двух совместно используемых функций может не хватить— окно восстанавливается, но вперед не выводится. Запутанность имеет оправдания (редкий случай!): в ранних версиях Windows разработчики прикладных программ настолько злоупотребляли возможностью вывести свое приложение поверх всех окон, что создателям Windows пришлось ограничить их активность на этом поприще. Вместо активизации — или вместе с ней — можно заставить заголовок окна на панели задач мигать— если вы хотите воспользоваться этой возможностью, то ищите в справке описание функции Fiashwindow. В нашем же случае, когда окно свернуто в иконку, ни одна из подобных функций не сработает, потому мы не будем далее задерживаться на этом вопросе.

Заметки на полях

В ряде источников (см. например, [38]) можно встретить более простой метод

решения проблемы повторного запуска, состоящий из вызова всего одной

функции FindWindow Кгсласс окна>, <заголовок окна>]. Мы будем использовать в дальнейшем эту функцию для поиска приложения (например, см. главу 7), однако приведенный метод хотя и более громоздкий, зато не требует знания класса окна, потому является более простым для понимания и применения. Заметим, что в таком виде данная процедура может пригодиться и для других надобностей (например, см. главу 14). Во избежание возможных ошибок при ее использовании, напомню еще раз, что любая процедура поиска окна по его названию чувствительна к регистру букв. Есть и не столь "лобовые" методы решения задачи идентификации своей программы — выбранный нами не слишком хорош тем, что в принципе может потребоваться менять по ходу дела имена приложения и тем болеа отдельного его окна. Другой метод предлагает, скажем, Юрий Зотов (http://www.delphikingdom.ru/asp/viewitem.aBp7catalogidB903). и состоит он в использовании memory mapped files — механизма отображения файлов на память. То есть в самом нвчапе программы вы создаете в общей памяти некий уникальный файл, а затем просто проверяете — существует ли он уже. Но я считаю, что во всех случвях нужно выбирать самое простое и понятное решение.

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

По теме:

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