Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2004.08.15;
Скачать: [xml.tar.bz2];

Вниз

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

 
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 вся ветка

Форум: "WinAPI";
Текущий архив: 2004.08.15;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.56 MB
Время: 0.038 c
14-1091197323
menart
2004-07-30 18:22
2004.08.15
Проблемы админов и программистов


14-1091210795
STALKER
2004-07-30 22:06
2004.08.15
Можт супертупо...


3-1090415150
AlexanderSK
2004-07-21 17:05
2004.08.15
Как правильнее использовать транзакции?


8-1086101616
ki11er
2004-06-01 18:53
2004.08.15
Порезать AVI


1-1091021969
mouse_web
2004-07-28 17:39
2004.08.15
PageControl





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский