![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Ограничение числа экземпляров
TStas © (2006-03-20 14:51:00)Читал сегодня статью с форума на эту тему, много это обсуждалось. ОДно мне непонятно: чем вариант с инишкой плох. СПециально только что провери - все отлично работает.
Вот файл проекта:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas', {Form1}
IniFiles, SysUtils;
{$R *.res}
var
ini: TINiFile;
b: Boolean;
begin
ini:=TINiFile.Create(ExtractFilePath(Application.ExeName)+'Run.ini');
B:=Ini.ReadBool('Runned', 'Already', false);
Ini.WriteBool('Runned', 'Already', true);
if b then
begin
Ini.Free;
Halt;
end;
Ini.Free;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. //В нем -то и чииается инишка
А вот файл формы, в нем инишка ставится на место
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses IniFiles;
{$R *.dfm}
procedure TForm1.FormDestroy(Sender: TObject);
var
ini: TIniFile;
begin
ini:=TINiFile.Create(ExtractFilePath(Application.ExeName)+'Run.ini');
Ini.WriteBool('Runned', 'Already', false);
Ini.Free;
end;
end.
ВСе работает. МОжет, в каких-то сложных случаях что-то не так?
Piter (2006-03-24 17:00:00) [61]Это только один из вариантов развития событий. Без исходников винды тут точно не скажешь.
Да и очень вероятно, что в разных версиях Windows события будут развиваться по разному пути даже при совершенно одинаковых условиях.
Piter (2006-03-24 16:59:00) [62]Хороший код, я бы так не догадался написать :)
Не учел бы, что винда такая умная и будет диспетчеризировать сообщения потока, когда он захвачен вызовом SendMessage.
Ну да ладно :)
Вот алгоритм ложной работы данного кода:
1) запускается первая копия, создается окно, выполняется дальнейший код "на пути" к:
SendMessage(HWND_BROADCAST...
2) в это время "параллельно" стартует вторая копия, тут важно, чтобы она успела создать свое окно ДО того, как в первой копии дело дойдет до "SendMessage(HWND_BROADCAST".
И не говорите, что такое невозможно - никто этого не знает. Возможно, RegisterWindowMessage будет долго обрабатываться в условиях тормозящей машины, иметь маленький приоритет.
В любом случае, существует ненулевая вероятность ситуации, когда оба приложения создадут окна, но ни в одном из них не дойдет до вызова "SendMessage(HWND_BROADCAST"
3) в первой копии доходит до вызова:
SendMessage(HWND_BROADCAST, Server_START_UP, Handle,0);
поток засыпает, винда пошла перебирать окна в системе
4) в это время во второй копии приложения исполнение кода тоже подходит к SendMessage, происходит вызов
5) вот тут то и диспетчеризируется броадкаст запрос от первой копии. Произойдет вызов TForm1.WndProc во второй копии, первой копии пошлется:
SendMessage(msg.WParam , WM_DONTINITIALIZE, Handle, 0);
так как поток первой копии находится в состоянии вызова системной функции SendMessage, то вполне возможно сообщение будет тут же диспетчеризировано. В первой копии установится флаг:
CanInitialize := false;
Далее второй копии будет послано сообшение WM_BRINGTOFRONT
6) поток второй копии также находится в режиме вызова системной функции SendMessage, будет исполнено: Application.BringToFront;
7) далее управление вернется к SendMessage, вызванной второй копией, будет разослан броадкаст от лица уже второй копии
8) так как первая копия все еще находится в состоянии вызова SendMessage - то сообщение будет диспетчеризировано тут же и в первой копии сработает: TForm1.WndProc
Второй копии будет выслано:
SendMessage(msg.WParam , WM_DONTINITIALIZE, Handle, 0);
9) ну вторая тоже вызывает SendMessage, поэтому сообщение также будет диспетчеризировано и во второй копии:
CanInitialize := false;
с последующими действиями и вызовом Application.BringToFront; в первой копии
10) далее произойдет выход из:
SendMessage(HWND_BROADCAST, Server_START_UP, Handle,0);
во второй копии, проверка на CanInitialize и Halt.
11) после чего произойдет выход из SendMessage в первой копии, проверка на CanInitialize и также Halt.
Defunct (2006-03-24 14:02:00) [63]
const
WM_DONTINITIALIZE = WM_USER + 0;
WM_BRINGTOFRONT = WM_USER + 1;
TForm1 = class( TForm )
private
Server_START_UP : Integer;
CanInitialize : Boolean;
procedure WndProc(var msg: TMessage); override;
procedure ProgramLeave(var msg:TMessage);message WM_DONTINITIALIZE;
procedure ProgramBringToFront(var Msg:TMessage);Message WM_BRINGTOFRONT;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
CanInitialize := True;
Server_START_UP := RegisterWindowMessage('WinRoute_Inspector StartUP message');
if Server_START_UP = 0 then
ShowMessage('Не удалось зарегистрировать сообщение'+#13+#10+
'для пресечения повторного запуска программы');
SendMessage(HWND_BROADCAST, Server_START_UP, Handle,0);
if not CanInitialize then
Halt(0);
....
end;
procedure TForm1.WndProc;
begin
if (msg.Msg = Server_START_UP) and (Handle <> msg.WParam) then
SendMessage(msg.WParam , WM_DONTINITIALIZE, Handle, 0);
if (msg.Msg = TaskbarCreated) then
if Config.PlacedToSystemTray then
PlaceOnSystemTray;
inherited;
end;
procedure TForm1.ProgramLeave;
begin
msg.Result := 777;
CanInitialize := false;
SendMessage( msg.WParam, WM_BringToFront, Handle, 0);
end;
procedure TForm1.ProgramBringToFront;
begin
msg.Result := 777;
Show;
Application.BringToFront;
end;
Piter (2006-03-24 13:19:00) [64]Defunct © (24.03.06 2:12) [32]
я могу привести код, а ты если есть желание попробуешь его "обломать".
приводи.
Defunct (2006-03-24 02:12:00) [65]>> Вначале сэмулируйте ситуацию когда на одном процессоре одновременно стартанут две копии
> Влегкую. Твой способ основан на сообщениях, никаких сообщений не будет без цикла выборки сообщений. А инициализация некоторых приложений может занимать существенное время, поэтому время выборки очередного сообщения может весьма продолжительным. За это время много что может случиться. Explorer как процесс с повышенным приоритетом вполне может успеть запустить другой процесс.
Причем тут вытесняющие потоки? Понятно что старт может быть прерван, однако так или иначе одна программа создаст основное окно раньше чем другая и очередь к этому окну будет создана раньше у другой копии программы. Броадкаст посылать лишь только после создания основного окна программы. Программа, которая первая создаст основное окно и победит в такой реализации.
> Я уже приводил пример, что мне удавалось открыть пару окон установления dial-up соединения, что не должно происходить в при нормальном режиме невозможно. Налицо просчет данной реализации.
Налицо кривизна рук и не более того.
Обрати внимание, что вместо результата на запрос броадкаста у меня посылается сообщение запрашивающей форме (sendmessage к конкретному окну), это сообщение в свою очередь приводит к закрытию запрашивающей программы. В худшем случае теоретически закрылись бы обе "стартовавшие одновременно" программы, но с учетом реализации механизма сообщений в Windows даже на многопроцессорной системе одна копия останется.
PS: я могу привести код, а ты если есть желание попробуешь его "обломать".
Piter (2006-03-24 01:37:00) [66]Defunct © (23.03.06 23:50) [30]
Вначале сэмулируйте ситуацию когда на одном процессоре одновременно стартанут две копии
Влегкую. Твой способ основан на сообщениях, никаких сообщений не будет без цикла выборки сообщений. А инициализация некоторых приложений может занимать существенное время, поэтому время выборки очередного сообщения может весьма продолжительным. За это время много что может случиться. Explorer как процесс с повышенным приоритетом вполне может успеть запустить другой процесс.
А уж на сильно загруженным компьютере вероятность такой ситуации повышается в разы.
Я уже приводил пример, что мне удавалось открыть пару окон установления dial-up соединения, что не должно происходить в при нормальном режиме невозможно. Налицо просчет данной реализации.
И эффекты очень забавные. Когда одно "окно" установило соединение, и ты жмешь соединиться во втором.
Никто не спорит, что реализовывать можно по разному, спор идет как идеологически правильно, чтобы не допускать ошибки в принципе. Можно использовать технологию, которая дает 99% вероятность успеха, если нет других технологий.
Но если есть вариант 100% безошибочной работы - то использовать желательно его, спорить никто не будет?
Defunct (2006-03-23 23:50:00) [67]Piter © (23.03.06 16:44) [28]
Вначале сэмулируйте ситуацию когда на одномпроцессоре одновременностартанут две копии. Потом можно будет продолжить полемику.
Приблизительно одинаковое время здесь откровенно "не катит", т.к. та копия которая стартанет приблизительно раньше, та и останется.
deamon_t (2006-03-23 17:16:00) [68]Имхо, вероятность этого исчезающие мала
Piter (2006-03-23 16:44:00) [69]Defunct © (23.03.06 12:57) [27]
и что будет, если две копию стартанут в приблизительно одинаковое время? Обе разошлют броадкаст, обе его поймают и обе закроются.
Defunct (2006-03-23 12:57:00) [70]> TStas
Я для этой цели использую RegisterWindowMessage, при старте посылаю сообщение всем окнам, если в системе уже запущена копия программы, то словив это сообщение она выдвигает себя на передний план, и отсылает только что запущенной программе сообщение, чтобы та себя закрыла.
Гаврила (2006-03-23 12:27:00) [71]
> Leonid Troyanovsky ©
> Piter ©
Ну ладно, убедили
:-)
Leonid Troyanovsky (2006-03-23 08:58:00) [72]
> Гаврила © (22.03.06 20:36) [22]
> > не начнет ее исполнять, пока первый оттуда не вышел.
> А процесс?
Для любых потоков, в том числе, из разных процессов.
--
Regards, LVT.
Пупкин (2006-03-22 23:08:00) [73]Форматируешь винт и этим ограничиваешь число. Делов-то
Piter (2006-03-22 21:06:00) [74]Гаврила © (22.03.06 19:18) [20]
Какая разница, это одна функция, или это не одена функция?
Это одна функция, представляющая собой кусок кода, реализованный где-то там в DLL
принципиальная. "где-то там в DLL" она реализована ТАК, что невозможно получить ошибку, ты так НЕ сможешь сделать в своем процессе или тебе надо будет писать драйвер.
Гаврила (2006-03-22 20:36:00) [75]
> Leonid Troyanovsky ©
> Важно лишь то, что это атомарная функция, т.е. второй
>поток
> не начнет ее исполнять, пока первый оттуда не вышел.
А процесс?
Leonid Troyanovsky (2006-03-22 19:40:00) [76]
> Гаврила © (22.03.06 19:18) [20]
> Какая разница, это одна функция, или это не одена функция?
Важно лишь то, что это атомарная функция, т.е. второй поток
не начнет ее исполнять, пока первый оттуда не вышел.
--
Regards, LVT.
Гаврила (2006-03-22 19:18:00) [77]
> Piter ©
> у тебя элементарно нету ОДНОЙ функции, которая может
> проверить наличие окна и создать новое
Какая разница, это одна функция, или это не одена функция?
Это одна функция, представляющая собой кусок кода, реализованный где-то там в DLL или это кусок кода, реализованного тут же (пусть и не одной функцией с точки зрения паскаля).
Тем более что все равно что-то случилось между моментом запуска процесса и подходу к этому "узкому месту", и это, случившееся, явно не "одна функция" с точки зрения паскаля
Разумеется, если написать инициализацию так, что реальный Handle будет выделен после кучи дополнительных мероприятий (типа а-ля подключение к БД), мы увеличим вероятность такой неудачи. Но это не имеет принципиального отличия от "чистой ситуации".
Leonid Troyanovsky (2006-03-22 15:18:00) [78]
> Piter © (21.03.06 20:27) [16]
> Повторная проверка? Отлично, но может случится ситуация,
> что каждая из копий создала окно и проводит повторную проверку.
> .. Обнаруживает уже запущенную копию и... ? Оба приложения
> закрываются, в результате ни одного запущенного процесса
> :)))
Такое впечатление, что ты возражаешь кому-то другому.
Я, собс-но, не противопоставлял FindWindow тому же,
если угодно, мьютексу. Просто назвал его для пущей
важности (или общности) именованным объектом.
Одному нужен семафор, другому событие, третьему - мьютекс.
Лично я предпочитаю проекцию файла.
> Конечно, чем больше таких повторных проверок и т.д. - тем
> шансы ошибки все больше стремятся к нулю.
Да, Бог с ней, с повторной проверкой.
Покажем сообщение и закроемся.
> Комбинация же FindForm, CreateForm такого не гарантирует
> нифига.
Ей и не нужно ничего гарантировать. Ее задача отыскать
первую копию (для активации ли, передачи ли параметров и др.).
Это, так сказать, плата за использование мьютекса (события,
семафора и т.д.) вместо проекции файла.
--
Regards, LVT.
Piter (2006-03-21 20:29:00) [79]Блин, и вместо FindForm я имел в виду FindWindow... ну торопился, мысль, думаю, понятна.
Piter (2006-03-21 20:28:00) [80]Piter © (21.03.06 20:27) [16]
у тебя элементарно нету функции, которая может проверить наличие окна и создать новое
* у тебя элементарно нету ОДНОЙфункции, которая может проверить наличие окна и создать новое