Форум: "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.052 c