CodeNet / Языки программирования / Delphi & Kylix / Система
Общение между запущенными копиями своих программ.
Автор: Квэнди
Рассматривать задачу будем на конкретном примере некого приложения.
Опишем предметную область и постановку задачи:
необходимо чтобы наше приложение, зарегистрировав себя как протокол в системе Windows, позволяло при нажатии на ссылкуу вида testproject:\\xxxxx запустило наше приложение (если оно не запущено) и передало ему параметры ссылки. Если же приложение уже запущено, то нам не обходимо запущеной копии приложения сообщить параметры ссылки.
Ну чтож, задачу описали, приступим к реализации.
Для примера я буду использовать BDS 2006.
Создадим новый проект.
Сначала нам необходимо зарегистрироваться в реестре, чтобы система воспринимала правильно наши ссылки, поэтому:
в uses главной формы дописываем модуль registry
в событии onactivate главной формы пишем:
procedure Tfstart.FormActivate(Sender: TObject); var reg:tregistry; begin reg:=tregistry.Create; reg.RootKey:=HKEY_Classes_Root; if not(reg.KeyExists('testproject')) then begin reg.OpenKey('testproject',true); reg.WriteString(",'URL:testproject Protocol'); reg.WriteString('URL Protocol',"); reg.OpenKey('DefaultIcon',true); reg.WriteString(",application.ExeName); reg.CloseKey; reg.OpenKey('testproject\shell\open\command',true); reg.WriteString(",application.ExeName+' %1?); reg.CloseKey; end; reg.Free; end;
Соответственно мы имеем зарегистрированный в системе протокол под названием testproject.
Проверить это можно достаточно простым способом:
Открываем любой браузер и в адресной строке набираем "testproject:\\eee" и запустится ваша программа.
Теперь продолжим. Нам необходимо опеределять запущена ли наша программа уже или нет. Для решения подобной задачи существует множество способов, но я предпочитаю способ с использованием mutex-ов. Не буду сейчас вдавать в подробности описания мьютексов и их использования. Итак, заходим в код самого нашего проекта и пишем там:
program testproject; uses Forms,windows, Ustart in 'Ustart.pas' {fstart}; {$R *.res} var HM: THandle; function Check: boolean; begin HM := OpenMutex(MUTEX_ALL_ACCESS, false, 'TestProjectMutex'); Result := (HM <> 0); if HM = 0 then HM := CreateMutex(nil, false, 'TestProjectMutex'); end; begin Application.Initialize; Application.CreateForm(Tfstart, fstart); Application.Run; end.
Итак, в результате у нас есть функция, возвращающая true если копия проекта запущена и false если это первая копия.
Теперь далее: нам необходимо понять запущено ли приложение с сылки или просто кто- то запустил наш exe.
Проверяется это достаточно просто: при запуске с сылки в наше приложение будет передан параметр коммандной строки, в котором будет сдержаться полностью строка ссылки, поэтому нам необходимо здесь обработать только один вариант: когда программа запущена с сылки и это уже не первая копия программы, т.е. пишем условие:
if (check)and(paramcount>0) then begin end;
Итак, что же мы будем делать если такой процесс уже есть? Нам соответсвенно необходимо каким- то образом сообщить запущенному процессу те параметры, которые нам передали. Отсюда встает вопрос: нам необходимо знать Handle нашего уже запущенного приложения. Здесь все подвластно исключительно вашей фантазии, так как сделать это можно сколь угодно множеством способов. Я выберу далеко не лучший, но для примера: я буду хранить handle в реестре. для этого модифицируем сначала onactivate нашей главной формы:
procedure Tfstart.FormActivate(Sender: TObject); var reg:tregistry; begin reg:=tregistry.Create; reg.RootKey:=HKEY_Classes_Root; if not(reg.KeyExists('testproject')) then begin reg.OpenKey('testproject',true); reg.WriteString(",'URL:testproject Protocol'); reg.WriteString('URL Protocol',"); reg.OpenKey('DefaultIcon',true); reg.WriteString(",application.ExeName); reg.CloseKey; reg.OpenKey('testproject\shell\open\command',true); reg.WriteString(",application.ExeName+' %1?); reg.CloseKey; end; reg.RootKey:=HKEY_current_user; reg.OpenKey('software\testproject',true); reg.WriteInteger('handle',fstart.Handle); reg.CloseKey; reg.Free; end;
далее добавим обработчик события CloseQuery нашей формы:
procedure Tfstart.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var reg:tregistry; begin reg:=tregistry.Create; reg.RootKey:=HKEY_current_user; reg.OpenKey('software\testproject',true); reg.WriteInteger('handle',0); reg.CloseKey; reg.Free; end;
таким образом при закрытии программы мы будем обнулять наш handle.
теперь вернемся к коду нашего проекта. Итак, нам необходимо если это не первая копия приложения, прочитать handle и отправить ему сообщение с параметрами коммандной строки. Приведу сразу код:
program testproject; uses Forms,windows,registry,sysutils,messages, Ustart in 'Ustart.pas' {fstart}; {$R *.res} var HM,HForm: THandle; reg:tregistry; ParamCmd:TCopyDataStruct; function Check: boolean; begin HM := OpenMutex(MUTEX_ALL_ACCESS, false, 'TestProjectMutex'); Result := (HM <> 0); if HM = 0 then HM := CreateMutex(nil, false, 'TestProjectMutex'); end; begin if (check)and(paramcount>0) then begin
{Читаем handle запущенного приложения}
reg:=tregistry.Create; reg.RootKey:=HKEY_current_user; reg.OpenKey('software\testproject',true); HForm:=reg.ReadInteger('handle'); reg.CloseKey; reg.Free;
{Состовляем структуру данных ParamCmd}
with ParamCmd do begin dwData := 0; cbdata:=strlen(pchar(paramstr(1)))+1; lpData:=pchar(paramstr(1)); end;
{Посылаем сообщение запущеной программе}
SendMessage(HForm, WM_COPYDATA,application.Handle,longint(@ParamCmd)); Exit; end; Application.Initialize; Application.CreateForm(Tfstart, fstart); Application.Run; end.
Вот собственно и почти все. Сейчас наше приложение умеет уже обрабатывать полученные данные и отправлять ихзапущенной копии приложения.
Но нам так же необходимо, чтобы наша запущенная копия получила эти данные, поэтому дополним код модуля нашей основной формы:
unit Ustart; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,registry, StdCtrls; type Tfstart = class(TForm) Label1: TLabel; procedure FormActivate(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); protected procedure Getmessage(var msg: TWMCopyData); message WM_COPYDATA; private { Private declarations } public { Public declarations } end; var fstart: Tfstart; implementation {$R *.dfm} procedure Tfstart.FormActivate(Sender: TObject); var reg:tregistry; begin reg:=tregistry.Create; reg.RootKey:=HKEY_Classes_Root; if not(reg.KeyExists('testproject')) then begin reg.OpenKey('testproject',true); reg.WriteString(",'URL:testproject Protocol'); reg.WriteString('URL Protocol',"); reg.OpenKey('DefaultIcon',true); reg.WriteString(",application.ExeName); reg.CloseKey; reg.OpenKey('testproject\shell\open\command',true); reg.WriteString(",application.ExeName+' %1?); reg.CloseKey; end; reg.RootKey:=HKEY_current_user; reg.OpenKey('software\testproject',true); reg.WriteInteger('handle',fstart.Handle); reg.CloseKey; reg.Free; if paramcount>0 then // Если это первая копия программы то мы можем сразу смело // обрабатывать наши параметры label1.Caption:=paramstr(1); end; procedure Tfstart.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var reg:tregistry; begin reg:=tregistry.Create; reg.RootKey:=HKEY_current_user; reg.OpenKey('software\testproject',true); reg.WriteInteger('handle',0); reg.CloseKey; reg.Free; end; procedure tfstart.Getmessage(var msg: TWMCopyData); var sText: array[0..99] of Char; begin // Преобразуем полученные данные в строку StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData); label1.Caption:=stext; end; end.
Вот собственно и все можем достаточно просто проверить:
Запустим первую копию нашего приложения, а затем в браузере наберем testproject:\\TEST и увидим как на нашей запущенной форме покажется этот текст.
Надеюсь что эта информация пригодится вам.
Комментарии можно оставить в блоге автора