Консультация № 174172
13.11.2009, 11:12
35.00 руб.
0 17 1
Здравствуйте. Вопрос следующий:
Необходимо заблокировать запуск второго экземпляра программы, написанной на Delphi6 и в случае, если первая запущенная копия свернута, то развернуть ее.

Приведенный ниже код только блокирует запуск второго экземпляра программы. А как реализовать вторую часть задачи?




Приложение:
uses
Forms,
Windows,
main in 'main.pas' {frm_main}
var
EventHandle : THandle;
const
EventName = 'one_inst_demo_event';

begin
// Пробуем открыть Event по имени
EventHandle := OpenEvent(EVENT_ALL_ACCESS, false, EventName);
if EventHandle <> 0 then begin
// Копия нашего приложения уже запущена - Event уже есть
CloseHandle(EventHandle);
halt;
end;
// Создаем Event
EventHandle := CreateEvent(nil, false, false, EventName);
Application.Initialize;
Application.CreateForm(TDM, DM);
Application.CreateForm(Tfrm_main, frm_main);
Application.Run;
// Уничтожаем наш Event при завершении приложения
CloseHandle(EventHandle);
end.

Обсуждение

Неизвестный
13.11.2009, 12:05
общий
Lekasa:
Здравствуйте, Lekasa!
Я предлагаю такой ответ на Ваш вопрос.
uses
Forms,
Windows,
main in 'main.pas' {frm_main}
var
EventHandle : THandle;
const
EventName = 'one_inst_demo_event';

begin
// Probuem otkryt' Event po imeni
EventHandle := OpenEvent(EVENT_ALL_ACCESS, false, EventName);
if EventHandle <> 0 then begin
// Kopiya nashego prilozheniya uzhe zapusсhena - Event uzhe est'
CloseHandle(EventHandle);
halt;
end;
// Sozdaem Event
EventHandle := CreateEvent(nil, false, false, EventName);
Application.Initialize;
Application.CreateForm(TDM, DM);
Application.CreateForm(Tfrm_<WBR>main, frm_main);
Application.MainForm.WindowState:=wsNormal;
Application.Run;
// Unichtozhaem nash Event pri zavershenii prilozheniya
CloseHandle(EventHandle);
end.

С уважением, Юсупов Роман.
Неизвестный
13.11.2009, 12:06
общий
это ответ
Здравствуйте, Lekasa.
Я предлагаю Вам вот такой вот вариант:

Приложение:
program Project123;

uses
Forms, ShellAPI, Windows,
Unit123 in 'Unit123.pas' {Form1};

{$R *.res}
var
h:word;
begin
// ищем окно приложения (если оно уже запущено, то мы его найдем и переменная h будет отлична от нуля
h:=FindWindow('TForm1'{Класс главной формы вашего приложения},
PChar('Form123'{Caption главной формы вашего приложения}));
If h>0 then
begin
// если нашли, то посылаем команду "RESTORE".
ShowWindow(h,SW_RESTORE);
// окно на передний план
SetForegroundWindow(h);
end else begin
// если не нашли, запускаем приложение (создаем формы и т.п.)
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
5
Неизвестный
13.11.2009, 13:29
общий
Yusupov Roman, к сожалению ваш медот не подходит по одной причине:
строка
Application.MainForm.WindowState:=wsNormal; может сработать только для второго запуска приложения, но не как ни для первого. Т.к. вторая копия не запустится, то эта строка теряет всякий смысл.
Неизвестный
13.11.2009, 13:31
общий
хотелось бы в цикле:
if EventHandle <> 0 then
begin
.......
end
использовать: if IsIconic(Application.Handle)
но как получить Handle первого запуска программы?
Неизвестный
13.11.2009, 13:37
общий
Lekasa:
Lekasa, странно, я же ответил на Ваш вопрос. К тому же в моем ответе содержится и ответ на Ваш вопрос по поводу получения Handle приложения.
Неизвестный
13.11.2009, 13:45
общий
Меня тоже интересует этот вопрос, а еще интересует вариант когда первый экземпляр приложения свернут в трей и окна скрыты.
Второй экземпляр программы не может найти окно приложения функцией FindWindow когда приложение свернуто в трей и развернуть его.
Неизвестный
13.11.2009, 14:11
общий
Paul80, используя ваш метод получилось:
uses
Forms,
Windows,
main in 'main.pas' {frm_main}
var
EventHandle : THandle;
h:word;
const
EventName = 'one_inst_demo_event';

begin
// Пробуем открыть Event по имени
EventHandle := OpenEvent(EVENT_ALL_ACCESS, false, EventName);
if EventHandle <> 0 then begin
// Копия нашего приложения уже запущена - Event уже есть
h:=FindWindow('Tfrm_main',PChar('Программа обо всем'));
If h>0 then
begin
ShowWindow(h,SW_RESTORE);
SetForegroundWindow(h);

end;
CloseHandle(EventHandle);
halt;
end;
// Создаем Event
EventHandle := CreateEvent(nil, false, false, EventName);
Application.Initialize;
Application.CreateForm(TDM, DM);
Application.CreateForm(Tfrm_main, frm_main);
Application.Run;
// Уничтожаем наш Event при завершении приложения
CloseHandle(EventHandle);
end.

Но есть несколько НО:
1. если окно свернуто, то оно развертывается не в полноэкранный режим просмотра.
2. самое страшное: при восстановлении из свернутого окна - свернуть его назад невозможно.
3. Caption может меняться, т.к. может быть прописана версия программы.
4. соглашусь с VolRus, что функция FindWindow не может найти окно приложения когда приложение свернуто в трей и окна скрыты.

Можно ли побороть 1 и 2 пункты?
Неизвестный
13.11.2009, 14:22
общий
Здравствуйте.

Можно блокировать запуск 2й копии из уже запущенного приложения. Для этого нужно поставить глобальный Hook на сообщения. В случае попытки создания 2го окна своего приложения не передавать дальше сообщение.(Отслеживать либо класс,либо Handle)

Пример локального(только для своего приложения) Hookа. Целевое сообщение изменение раскладки клавиатуры.

Установка Hook'а
Код:
setWindowsHookEx(wh_GetMessage,@msghook,0,GetcurrentThreadId);


процедура Hook'a
Код:

function msghook(code:integer;wp:wparam;lp:lparam):lresult;stdcall;
var
Layout: array [0.. KL_NAMELENGTH] of char;
begin
result:=callNextHookEx(WH_GETMESSAGE,code,wp,lp);
if TMsg(Pointer(lP)^).message=Wm_InputlangChangeRequest then
begin
GetKeyboardLayoutName(Layout);
if layout='00000419' then form1.Label4.Caption:='EN'
else form1.Label4.Caption:='RU'
end;

end;

Описание механизма Hook'ов есть в справке Delphi. В интернете полно примеров клавиатурных перехватчиков на основе Hook'a клавиатуры.
Неизвестный
13.11.2009, 14:32
общий
Lekasa:
1
нужно разворачивать обязательно во весь экран? Если да, то замените SW_RESTORE на SW_SHOWMAXIMIZED. Команда SW_RESTORE восстанавливает то положение окна, какое было до его сворачивания, т.е. если оно было максимизировано и затем свернуто, то оно после команды SW_RESTORE вернется в максимизированное состояние.
2
я сейчас еще посмотрю... но у меня все работает! нормально сворачивается по кнопочке. У меня Vista, может по этому нормально работает.
3
измените h:=FindWindow('Tfrm_main',PChar('Программа обо всем')); на h:=FindWindow('Tfrm_main',nil);
4
гм, находит фунция FindWindow скрытые окна. она не находит дочерние окна, это да, для этого есть другая функция.
Неизвестный
13.11.2009, 14:45
общий
подумал еще, может причина нашего с вами разного поведения приложения в том что у меня дельфи 2010?
Неизвестный
13.11.2009, 15:49
общий
Paul80, вернусь к пункту 2:
наблюдение следующее: если окно первого запуска не свернуто, а находится за другими окнами, то в случае запуска второй копии приложения- первая копия становится главным окном и кнопочка свернуть работает нормально. Только стоит свернуть первое приложение, при запуске второй копии - первая разворачивается, но свернуть - уже невозможно. Есть еще какие-нибудь мысли по поводу этого?
Неизвестный
13.11.2009, 15:49
общий
Vadim22, интересная мысль, но нужно разбираться
Неизвестный
13.11.2009, 16:27
общий
Lekasa:
упраздните вообще кнопочку "свернуть"сворачивайте по кнопке "закрыть" (обрабатывайте событие OnClose формы), как Вам такой вариант?
Неизвестный
13.11.2009, 16:29
общий
А Hook setWindowsHookEx возможно установить без прав администратора? Если нет ну тогда это как по мне не лучший способ.

А может кто в курсе насчет FileMapping (один из способов обмена данными между процессами).
Может с помощью его можно подать команду приложению на развертывание с панели задач или трея?

http://www.realcoding.net/article/view/4470
Неизвестный
13.11.2009, 16:37
общий
Paul80,
не вариант :(
Неизвестный
13.11.2009, 20:51
общий
Lekasa:
еще один вариант:

program Project123;

uses
Forms, ShellAPI, Windows, Messages,
Unit123 in 'Unit123.pas' {Form1};

{$R *.res}
var
h:word;

const
MYRESTORE = WM_USER+875;


begin
h:=FindWindow('TForm1'{Класс главной формы вашего приложения},
PChar('Form123'{Caption главной формы вашего приложения}));
If h>0 then
begin
// ShowWindow(h,SW_RESTORE); меняем на другую систему]
SendMessage(h,MYRESTORE,0,0);
end else begin
Application.Initialize;
Application.MainFormOnTaskbar := False;
Application.Title := '123';
Application.CreateForm(TForm1, Form1);
Application.Run;

end;
end.

!!!!!!!!!!!!! теперь файл модуля формы

unit Unit123;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

const
MYRESTORE = WM_USER+875;

type
TForm1 = class(TForm)
Button1: TButton;
procedure CommandMessageReciver(var msg:TMessage); message MYRESTORE;
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;


implementation

{$R *.dfm}

// Handler for message
procedure TForm1.CommandMessageReciver(var msg:TMessage);
begin
Application.Restore;
end;



end.
Неизвестный
16.11.2009, 16:34
общий
Paul80
Спасибо, заработало
Форма ответа