Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Ограничение числа экземпляров


Страницы:   1 2 3 4 5

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]
у тебя элементарно нету функции, которая может проверить наличие окна и создать новое


* у тебя элементарно нету ОДНОЙфункции, которая может проверить наличие окна и создать новое


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо