Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.08.15;
Скачать: CL | DM;

Вниз

Один инстанс   Найти похожие ветки 

 
vecna ©   (2004-07-06 15:19) [0]

народ, подкиньте ссылок, где разобраны варианты с не запуском более чем N копий программы одновременно.


 
Тимохов ©   (2004-07-06 15:21) [1]

www.delphimaster.ru + фак
поиск createmutex


 
Игорь Шевченко ©   (2004-07-06 16:13) [2]

Тимохов ©   (06.07.04 15:21)


>  с не запуском более чем N копий программы одновременно.


Эт тогда семафор


 
vecna ©   (2004-07-06 16:14) [3]

спасибо, но какие-то варианты "не очень"...
для каждого приложения придумывать уникальную строку - это плохо, можно осознать есть копия или нет, а сколько их запушено? нет оповешения ранее созданых инстансов что добавился еще один...
чувствую придется изобретать велосипед


 
Тимохов ©   (2004-07-06 16:17) [4]


> оповешения ранее созданых инстансов что добавился еще один

про это вы и не спрашивали.
> для каждого приложения придумывать уникальную строку - это
> плохо,

у вас с этим проблемы? guid не подойдет.

действительно, вмето mutex используейте самафор и нормально будет.


 
OSokin   (2004-07-06 21:19) [5]

Да можно и не только семафором, но и мутексами.


 
DeadMeat ©   (2004-07-06 21:49) [6]


> можно осознать есть копия или нет, а сколько их запушено?

Дж. Рихтер, "Windows для профессионалов", стр. 417. Там описание программы AppInst. Чуть назад принцип её работы... Насколько понял, там надо работать с заголовком *.EXE файла...

---
...Death Is Only The Begining...


 
Игорь Шевченко ©   (2004-07-07 10:47) [7]

OSokin   (06.07.04 21:19) [5]


> Да можно и не только семафором, но и мутексами


Мне было бы очень интересно узнать, каким образом мутексами модно добиться запуска не более 3-х экземпляров программы


 
cae ©   (2004-07-07 11:05) [8]

Я делал через EnumWindows (несколько для других целей)


 
evvcom ©   (2004-07-07 11:22) [9]


> каким образом мутексами модно добиться запуска не более
> 3-х экземпляров программы

В цикле искать свободный из 3-х. :))) Явно здесь семафор удобнее.


 
NAlexey ©   (2004-07-07 13:57) [10]

У себя я сделал так:

 
 PInstanceData = ^TInstanceData;
 TInstanceData = record
   Handle: HWND;
   UserName: string[255];
   SysDBName: string[255];
   DocDBName: string[255];
 end;

 PInstancesData = ^TInstancesData;
 TInstancesData = record
   InstancesCount: Integer;
   Instances: array[0..255] of TInstanceData;
 end;

function SameInstanceExists(const UserName: string): boolean;

var
 MapFile: THandle;
 InstancesData: PInstancesData;

const
 INSTANCES_LIST_NAME = "INSTANCES_LIST";

procedure MapFileMemory;
var
 ZeroMem: boolean;
begin
 MapFile := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
    SizeOf(TInstancesData), INSTANCES_LIST_NAME);
 if (MapFile = 0) then
 begin
   MessageBox(Application.Handle, "Could not create file map object.",
     "Ошибка", MB_OK);
 end else begin
   ZeroMem := GetLastError <> ERROR_ALREADY_EXISTS;
   InstancesData := MapViewOfFile(MapFile, FILE_MAP_ALL_ACCESS, 0, 0,
      SizeOf(TInstancesData));
   if (InstancesData = nil) then
   begin
     CloseHandle(MapFile);
     MessageBox(Application.Handle, Could not map file.",
       "Ошибка", MB_OK);
   end else if ZeroMem then
     FillChar(InstancesData^, SizeOf(TInstancesData), #0);
 end;
end;

procedure UnmapFileMemory;
begin
 if (InstancesData <> nil) then
 begin
   UnMapViewOfFile(InstancesData);
   InstancesData := nil;
 end;

 if (MapFile <> 0) then
 begin
   CloseHandle(MapFile);
   MapFile := 0;
 end;
end;

procedure AddCurrInstance(const UserName: string);
var
 CurrInstance: TInstanceData;
begin
 if (InstancesData <> nil) then
 begin
   FillChar(CurrInstance, SizeOf(TInstanceData), #0);
   CurrInstance.Handle := Application.Handle;
   CurrInstance.UserName := UserName;
   CurrInstance.SysDBName := SystemAlias;
   CurrInstance.DocDBName := DataAlias;
   InstancesData^.Instances[InstancesData^.InstancesCount] := CurrInstance;
   Inc(InstancesData^.InstancesCount);
 end;
end;

procedure RemoveInstance(Wnd: HWND);
var
 I: integer;
begin
 if (InstancesData <> nil) then
   for I := 0 to InstancesData^.InstancesCount - 1 do
     if InstancesData^.Instances[I].Handle = Wnd then
     begin
       if I < InstancesData^.InstancesCount - 1 then
         Move(InstancesData^.Instances[I + 1], InstancesData^.Instances[I],
            (InstancesData^.InstancesCount - I - 1) * SizeOf(TInstanceData));
       Dec(InstancesData^.InstancesCount);
       Break;
     end;
end;

function SameInstanceExists(const UserName: string): boolean;
var
 I: Integer;
begin
 Result := False;
 MapFileMemory;
 if (InstancesData <> nil) then
 begin
   with InstancesData^ do
   begin
     for I := 0 to InstancesData^.InstancesCount - 1 do
       if (AnsiUpperCase(InstancesData^.Instances[I].UserName) = AnsiUpperCase(UserName)) and
         (InstancesData^.Instances[I].SysDBName = SystemAlias) and
         (InstancesData^.Instances[I].DocDBName = DataAlias)
       then begin
         Result := True;
         MessageBox(Application.Handle, PChar("Пользователь с именем "" + UserName +
           "" уже подключен к базам "" + SystemAlias + "" и "" + DataAlias + ""."),
           "Ошибка", MB_OK or MB_ICONEXCLAMATION);
         Exit;
       end;
   end;
   AddCurrInstance(UserName);
 end;
end;

function SameInstanceExists(const UserName: string): boolean;
var
 I: Integer;
begin
 Result := False;
 MapFileMemory;
 if (InstancesData <> nil) then
 begin
   with InstancesData^ do
   begin
     for I := 0 to InstancesData^.InstancesCount - 1 do
       if (AnsiUpperCase(InstancesData^.Instances[I].UserName) = AnsiUpperCase(UserName)) and
         (InstancesData^.Instances[I].SysDBName = SystemAlias) and
         (InstancesData^.Instances[I].DocDBName = DataAlias)
       then begin
         Result := True;
         MessageBox(Application.Handle, PChar("Пользователь с именем "" + UserName +
           "" уже подключен к базам "" + SystemAlias + "" и "" + DataAlias + ""."),
           "Ошибка", MB_OK or MB_ICONEXCLAMATION);
         Exit;
       end;
   end;
   AddCurrInstance(UserName);
 end;
end;


 
KADAN ©   (2004-07-07 14:52) [11]

вообще-то это правда делается семафорами, хотя еще можно через сообщения. Прога запускается делает BroadCast всем приложениям, типа "есть ли здесь такие же как я?", а те ей отвечают: "тута мы". а она считает скока их накопилось уже, и если много - тогда завершается... аварийно... :)


 
NAlexey ©   (2004-07-07 14:55) [12]

>KADAN ©   (07.07.04 14:52) [11]
Ну так, хм... А если один из экземляров приложений в данный момент занят какими нибудь расчетами, и не может ответить?


 
KADAN ©   (2004-07-07 14:59) [13]

ну тады тока семафорами... или делать processmessages в "тяжелых" местах программы.


 
evvcom ©   (2004-07-07 15:24) [14]


> KADAN ©

А если приложение еще не создало очередь и не может принимать сообщение? Однозначно такие задачи - через глобальные объекты синхронизации, для этого случая лучше семафор.


 
Игорь Шевченко ©   (2004-07-07 15:25) [15]

Кстати, буквально сегодня встала задача ограничить число одновременно работающих экземпляров приложения до некоторого числа (связано с ограничениями серверов, с которыми это приложение соединяется).

Сделали очень просто:

var
 SemHandle: THandle;
.....
 SemHandle := CreateSemaphore (nil, MaxInstanceCount, MaxInstanceCount, SGlobalSemaphoreName);
 if SemHandle = 0 then
   RaiseLastWin32Error;
 WaitForSingleObject(SemHandle, INFINITE);
 try
..... код приложения ....
 finally
   ReleaseSemaphore(SemHandle, 1, nil);
   CloseHandle(SemHandle);
 end;


 
KADAN ©   (2004-07-07 15:32) [16]


> evvcom ©   (07.07.04 15:24) [14]

> А если приложение еще не создало очередь и не может принимать
> сообщение?

одно из двух запускаемых уже ее создало, и готово принимать ответы на свой BroadCast, а другое сделает это чуть попозже и ему скажут, что уже поздно.


 
y-soft ©   (2004-07-07 15:34) [17]

Одно старенькое решение:

http://www.delphimaster.ru/articles/limit.html

Недостаток: не работает, если приложение запускается из разных директорий

unit LimHinst;
//****************************************************************************
// Usage: модуль необходимо указать ПЕРВЫМ в списке uses файла .DPR проекта
// и установить необходимые значения констант HinstLimit и WaitPause.
//****************************************************************************
interface

const
  //Установите необходимые значения!!!
 HinstLimit = 1;
 WaitPause = 50;

implementation
uses
 Windows;
var
 Semaphore : THandle;
 SemaphoreName : array[0..260] of Char;
 IncCnt : integer;

function StopLoading : boolean;
var
 L,I : integer;
begin
 // В качестве уникального имени семафора используем полный путь
 // к исполняемому файлу приложения (по определению уникален!!!)
 L := GetModuleFileName(MainInstance,SemaphoreName,SizeOf(SemaphoreName));
 // В имени семафора нельзя использовать обратные слэши, поэтому
 // заменяем их на прямые (или еще на что-нибудь кроме #0)
 for I := 0 to L - 1 do
   if SemaphoreName[I] = "\" then
     SemaphoreName[I] := "/";

 Semaphore := CreateSemaphore(nil,HinstLimit,HinstLimit,SemaphoreName);

 Result := (Semaphore = 0) or // Если семафор не удалось создать
 (WaitForSingleObject(Semaphore,WaitPause) <> WAIT_OBJECT_0); // Если семафор занят
end;

procedure ShowErrMsg;
const
 PROGRAM_ALREADY_RUN = "Лимит исчерпан";

begin
MessageBox(0, PROGRAM_ALREADY_RUN, SemaphoreName, MB_ICONSTOP or
MB_OK);
end;

initialization
 IncCnt := 0;
 if StopLoading then
 begin
   ShowErrMsg;
   halt;
 end
 else
   IncCnt := 1;
finalization
 if Semaphore <> 0 then
 begin
   ReleaseSemaphore(Semaphore, IncCnt, nil);
   CloseHandle(Semaphore);
 end;
end.


 
evvcom ©   (2004-07-07 15:55) [18]


> KADAN ©   (07.07.04 15:32) [16]
> одно из двух запускаемых уже ее создало, и готово принимать
> ответы на свой BroadCast, а другое сделает это чуть попозже
> и ему скажут, что уже поздно.

Никто никому ничего не скажет, если запущено 2 приложения с интервалом доли секунды. Если только посылать это сообщение после того, как создана главная форма, но это бессмысленная трата драгоценных ресурсов.

> y-soft ©   (07.07.04 15:34) [17]

Стоит ли городить такой огород, если к тому же имеются недостатки.
Учитесь у Мастеров. См. [15] - коротко, ясно и надежно!


 
y-soft ©   (2004-07-07 16:01) [19]

>evvcom ©   (07.07.04 15:55) [18]

Создается впечатление, что Вы даже не смотрели код. Принцип тот же, что и в [15], но уникальное имя для семафора формируется автоматически. К тому же не надо ничего писать в DPR, достаточно поместить модуль в USES первым...


 
NAlexey ©   (2004-07-07 16:04) [20]

>Игорь Шевченко ©   (07.07.04 15:25) [15]
Красиво.


 
Игорь Шевченко ©   (2004-07-07 16:23) [21]

y-soft ©   (07.07.04 16:01)

У нас с тобой несколько разные задачи - у меня задача не завершать приложение, при невозможности обратиться к ресурсу, а просто подождать, пока он не освободиться. Кроме того, у меня максимальное число обновременно работающих приложений считывается из настроек, поэтому, увы, разместить код в секциях initialization, finalization не получится.

С уважением,


 
evvcom ©   (2004-07-07 16:23) [22]


> Создается впечатление, что Вы даже не смотрели код.

А я его и не смотрел. Сделал вывод по

> Недостаток: не работает, если приложение запускается из
> разных директорий

Мне этого хватило. Для ответа на любой вопрос достаточно двух-трех ключевых строк кода. В [15] немного больше, но там весь код программы, и написан он оптимально! Вот в таком коде приятно разбираться.
А Ваш код не умещается даже на экранную страницу! Разбираться в этом мне лень.


 
y-soft ©   (2004-07-07 16:54) [23]

>Игорь Шевченко ©   (07.07.04 16:23) [21]

Я писал универсальное решение для кнопкокидателей :)

Можно и заставить ждать освобождения ресурсов, если установить константу в INFINITE (я вырезал это замечание из исходного кода, чтобы меньше места занимал). Можно и читать значения из INI-файлов, реестра и т.п. Можно даже считывать уникальное имя для мьютекса, вместо того, чтобы "вращать слеши"... А Initialize потому, что при таком подходе есть одно важное достоинство - когда он выполняется, приложение еще не загрузило никаких ресурсов, особенно, если модуль стоит первым в USES

Надеюсь, ты не согласен с доводами, подобными [22]? :)))

В общем-то мьютексы для таких задач и придуманы...


 
Игорь Шевченко ©   (2004-07-07 17:05) [24]

y-soft ©   (07.07.04 16:54) [23]


> А Initialize потому, что при таком подходе есть одно важное
> достоинство - когда он выполняется, приложение еще не загрузило
> никаких ресурсов, особенно, если модуль стоит первым в USES


Неудобно, на мой взгляд. У меня, например, конфигурация считывается в трех других unit"ах :) А когда работа приложения зависит от порядка перечисления unit"ов, кроме того, что для одного явно указано, что он должен быть первым (пример с ShareMem) это уже не есть очень хорошо.


> Надеюсь, ты не согласен с доводами, подобными [22]? :)))


Не согласен.


> В общем-то мьютексы для таких задач и придуманы...


В общем-то, мьютексы придуманы для задач, которые гарантировано должны запускаться поодиночке :)
У меня несколько иная ситуация, сервер позволяет подключиться довольно большому числу клиентов, но с разных компьютеров. Количество подключений с одного компьютера ограничено.


 
y-soft ©   (2004-07-07 17:10) [25]

>Игорь Шевченко ©   (07.07.04 17:05) [24]

Ну так, сколько программистов - столько и решений :) "Универсальные" только в FAQ"ах :)


 
DiamondShark ©   (2004-07-07 17:16) [26]


> Игорь Шевченко ©   (07.07.04 15:25) [15]
> Кстати, буквально сегодня встала задача ограничить число
> одновременно работающих экземпляров приложения до некоторого
> числа (связано с ограничениями серверов, с которыми это
> приложение соединяется).
>
> Сделали очень просто:

И не верно.


 
Sandman25 ©   (2004-07-07 17:21) [27]

[26] DiamondShark ©   (07.07.04 17:16)

В смысле try на строчку выше надо?


 
DiamondShark ©   (2004-07-07 17:25) [28]


> Sandman25 ©   (07.07.04 17:21) [27]
> [26] DiamondShark ©   (07.07.04 17:16)
>
> В смысле try на строчку выше надо?

Не только.

try
  if WaitForSingleObject(SemHandle, 0) <> WAIT_OBJECT_0 then Exit;
..... код приложения ....
finally
  ReleaseSemaphore(SemHandle, 1, nil);
  CloseHandle(SemHandle);
end;


 
Sandman25 ©   (2004-07-07 17:27) [29]

Понятно.


 
DiamondShark ©   (2004-07-07 17:34) [30]

Но это тоже не правильно :)

if WaitForSingleObject(SemHandle, 0) <> WAIT_OBJECT_0 then
 begin
   CloseHandle(SemHandle);
   Exit;
 end;
try
..... код приложения ....
finally
 ReleaseSemaphore(SemHandle, 1, nil);
 CloseHandle(SemHandle);
end;


 
Sandman25 ©   (2004-07-07 17:37) [31]

Тогда уже так :)


try
 if WaitForSingleObject(SemHandle, 0) <> WAIT_OBJECT_0 then
   Exit;
 try
  ..... код приложения ....
 finally
   ReleaseSemaphore(SemHandle, 1, nil);
 end;
finally
CloseHandle(SemHandle);
end


 
Игорь Шевченко ©   (2004-07-07 17:41) [32]

DiamondShark ©   (07.07.04 17:25)

Читаем внимательно пост № 21, мне Exit не нужен. Смысла в переносе try на строчку выше я, честно говоря, не вижу. Было бы интересно узнать, в чем он.


 
Sandman25 ©   (2004-07-07 17:45) [33]

[32] Игорь Шевченко ©   (07.07.04 17:41)

WaitForSingleObject может завершиться по runtime ошибке. Тогда будет произведен ReleaseSemaphore, хотя захват не происходил.


 
DiamondShark ©   (2004-07-07 17:47) [34]


> Игорь Шевченко ©   (07.07.04 17:41) [32]
> DiamondShark ©   (07.07.04 17:25)
>
> Читаем внимательно пост № 21, мне Exit не нужен.

Читаем внимательно субж.


> Смысла в переносе try на строчку выше я, честно говоря,
> не вижу. Было бы интересно узнать, в чем он.

Читаем внимательно пост № 30


 
Игорь Шевченко ©   (2004-07-07 17:53) [35]

DiamondShark ©   (07.07.04 17:47)

А я решение не для Subj приводил, а для похожей задачи. О чем и написал, кстати :) Для Subj больше подходит решение y-soft, которое не позволяет запустить больше указанного числа приложений.


> Читаем внимательно пост № 30


Прочитал, не понял. Мне не нужно завершать приложение по недоступности ресурса, мне нужно подождать, пока ресурс не освободится.


 
Sandman25 ©   (2004-07-07 17:53) [36]

Упс. Получается, что в WaitForSingleObject exception сгенерится не может. Кода [30] достаточно.


 
DiamondShark ©   (2004-07-07 18:02) [37]


> Игорь Шевченко ©   (07.07.04 17:53) [35]
>
> А я решение не для Subj приводил, а для похожей задачи.
> О чем и написал, кстати :) Для Subj больше подходит решение
> y-soft, которое не позволяет запустить больше указанного
> числа приложений.

А, ну значит я просто не врубился. Кстати, не я один. Прочтите последующее обсуждение. Народ уверен, что решение именно для субж, а не для несколько изменённой задачи.


> Прочитал, не понял.

Я поправился. В [30] нет переноса трай на строчку.



Страницы: 1 вся ветка

Текущий архив: 2004.08.15;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.028 c
1-1090873774
Knight
2004-07-27 00:29
2004.08.15
Можно ли сделать сдвиг одновременно всех символов в строке...


1-1091115800
dprimakov
2004-07-29 19:43
2004.08.15
Размер любого файла


1-1091447690
Maxim
2004-08-02 15:54
2004.08.15
Особые символы


1-1091183687
goliath
2004-07-30 14:34
2004.08.15
CLX vs VCL


3-1090229620
AlexanderSK
2004-07-19 13:33
2004.08.15
Копирование данных?