Author: Lou Adler
I was surprised to find that I didn't have an article covering this topic - I wrote
a couple of 'em for inquiry.com a couple of years ago. Oh well... they're in
another place and time... In any case, what I'll give you here are two techniques
for handling previous instances of your applications.
Answer:
Oh! Were that only the phrase I used when I was single! Alas, like most, it
unfortunately wasn't. But at least I could apply that phrase in my professional
life; of course, in a totally different way. Okay, enough of the play on words.
There are many applications that lend themselves to having more than one copy
running at any time. But in my experience, most of the applications I build only
lend themselves to a single instance. For instance, it isn't practical to run more
than one instance of a data-entry application; especially when it updates local
data. In cases such as this, I limit the execution of another instance of a program
by executing some simple code.
What I'm going to show you is two different functions that do exactly the same
thing. The only difference between the two is that the first function can only be
run in Win32, and the other function can run in either Win16 or Win32. Here's the
code:
1
2 // ===================================================
3 // Called by your project file, prevents a 2nd
4 // instance of the program from executing and
5 // instead activates the already executing instance.
6 // Returns TRUE if a previous instance of the
7 // program is already running. Win32 ONLY
8 // ===================================================
9
10 function IsPrevInst: Boolean;
11 var
12 semName,
13 appClass: PChar;
14 hSem: THandle;
15 hWndMe: HWnd;
16 appTitle: array[0..MAX_PATH] of Char;
17 begin
18 // Init
19 Result := FALSE;
20 GetMem(semName, 15);
21 GetMem(appClass, 15);
22 StrPCopy(semName, 'SemaphoreName');
23 StrPCopy(appClass, 'TApplication');
24 StrPCopy(appTitle, ExtractFileName(Application.Title));
25
26 // Create a Semaphore in memory. If this is the
27 // first instance, then hSem's value should be 0.
28 hSem := CreateSemaphore(nil, 0, 1, semName);
29
30 // Check to see if the semaphore exists
31 if (hSem <> 0) and (GetLastError() =
32 ERROR_ALREADY_EXISTS) then
33 begin
34 CloseHandle(hSem);
35
36 // Get the current window's handle then change
37 // its title so we can look for the other instance
38 hWndMe := FindWindow(appClass, appTitle);
39 SetWindowText(hWndMe, 'ZZZZZZZ');
40
41 // Search for other instance of this window then bring
42 // it to the top of the Z-order stack. We find it by
43 // matching the Application Class and
44 // Application Title.
45 hWndMe := FindWindow(appClass, appTitle);
46 if (hWndMe <> 0) then
47 begin
48 BringWindowToTop(hWndMe);
49 ShowWindow(hWndMe, SW_SHOWNORMAL);
50 end;
51
52 Result := TRUE;
53 end;
54
55 // Destroy PChars
56 FreeMem(semName, 15);
57 FreeMem(appClass, 15);
58 end;
59
60 //This is a different twist on the previous example.
61 //It uses a mutex (MUTually EXclusive) instead of a sema-
62 //phore.
63
64 procedure CheckPrevInstEx(MainFormClassName,
65 MainFormCaption: string);
66 var
67 PrevWnd: HWnd;
68 Mutex: THandle;
69 begin
70 {$IFDEF Win32}
71 Mutex := CreateMutex(nil, False, 'InstanceMutex');
72 if WaitForSingleObject(Mutex, 10000) = WAIT_TIMEOUT then
73 Application.Terminate;
74 {$ELSE}
75 if HPrevInst = 0 then
76 Application.Terminate;
77 {$ENDIF}
78
79 PrevWnd := FindWindow(PChar(MainFormClassName),
80 PChar(MainFormCaption));
81 if PrevWnd <> 0 then
82 PrevWnd := GetWindow(PrevWnd, GW_OWNER);
83 if PrevWnd <> 0 then
84 begin
85 if IsIconic(PrevWnd) then
86 ShowWindow(PrevWnd, SW_SHOWNORMAL)
87 else
88 {$IFDEF Win32}
89 SetForegroundWindow(PrevWnd);
90 {$ELSE}
91 BringWindowToTop(PrevWnd);
92 {$ENDIF}
93 Application.Terminate;
94 end;
95 ReleaseMutex(Mutex);
96 CloseHandle(Mutex);
97 end;
To use the functions above, you can either embed them in the project file, or
better yet, place them in a globally accessible library for use in all your
applications that need them. Here's some example code for implementing them:
98 //This example uses the IsPrevInst function
99 program RxProto;
100
101 uses
102 Forms,
103 Main in 'Main.pas' {MainForm},
104 Proc in 'Proc.pas',
105 //This is my global library
106 UTIL32 in '..\Lib\UTIL\Util32.pas',
107 LoopPnThr in '..\Packages\LoopPnThr.pas';
108
109 {$R *.RES}
110
111 begin
112 if not IsPrevInst then
113 begin
114 Application.Initialize;
115 Application.CreateForm(TMainForm, MainForm);
116 Application.Run;
117 end
118 else
119 Application.Terminate;
120 end.
121 //Here's the other way...
122 program RxProto;
123
124 uses
125 Forms,
126 Main in 'Main.pas' {MainForm},
127 Proc in 'Proc.pas',
128 UTIL32 in '..\Lib\UTIL\Util32.pas',
129 LoopPnThr in '..\Packages\LoopPnThr.pas';
130
131 {$R *.RES}
132
133 begin
134 CheckPrevInstEx('TApplication', 'My Application');
135 //This code won't do anything if CheckPrevInstEx doesn't
136 //pass muster
137 Application.Initialize;
138 Application.CreateForm(TMainForm, MainForm);
139 Application.Run;
140 end.
As you can see, pretty simple stuff. Have fun with it!
|