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 2nd4 // instance of the program from executing and5 // instead activates the already executing instance.6 // Returns TRUE if a previous instance of the7 // program is already running. Win32 ONLY8 // ===================================================9 10 function IsPrevInst: Boolean;
11 var12 semName,
13 appClass: PChar;
14 hSem: THandle;
15 hWndMe: HWnd;
16 appTitle: array[0..MAX_PATH] of Char;
17 begin18 // Init19 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 the27 // first instance, then hSem's value should be 0.28 hSem := CreateSemaphore(nil, 0, 1, semName);
29 30 // Check to see if the semaphore exists31 if (hSem <> 0) and (GetLastError() =
32 ERROR_ALREADY_EXISTS) then33 begin34 CloseHandle(hSem);
35 36 // Get the current window's handle then change37 // its title so we can look for the other instance38 hWndMe := FindWindow(appClass, appTitle);
39 SetWindowText(hWndMe, 'ZZZZZZZ');
40 41 // Search for other instance of this window then bring42 // it to the top of the Z-order stack. We find it by43 // matching the Application Class and44 // Application Title.45 hWndMe := FindWindow(appClass, appTitle);
46 if (hWndMe <> 0) then47 begin48 BringWindowToTop(hWndMe);
49 ShowWindow(hWndMe, SW_SHOWNORMAL);
50 end;
51 52 Result := TRUE;
53 end;
54 55 // Destroy PChars56 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 var67 PrevWnd: HWnd;
68 Mutex: THandle;
69 begin70 {$IFDEF Win32}71 Mutex := CreateMutex(nil, False, 'InstanceMutex');
72 if WaitForSingleObject(Mutex, 10000) = WAIT_TIMEOUT then73 Application.Terminate;
74 {$ELSE}75 if HPrevInst = 0then76 Application.Terminate;
77 {$ENDIF}78 79 PrevWnd := FindWindow(PChar(MainFormClassName),
80 PChar(MainFormCaption));
81 if PrevWnd <> 0then82 PrevWnd := GetWindow(PrevWnd, GW_OWNER);
83 if PrevWnd <> 0then84 begin85 if IsIconic(PrevWnd) then86 ShowWindow(PrevWnd, SW_SHOWNORMAL)
87 else88 {$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;
98 99 {To use the functions above, you can either embed them in the project file, or 100 better yet, place them in a globally accessible library for use in all your 101 applications that need them. Here's some example code for implementing them:}102 103 //This example uses the IsPrevInst function104 program RxProto;
105 106 uses107 Forms,
108 Main in'Main.pas'{MainForm},
109 Proc in'Proc.pas',
110 //This is my global library111 UTIL32 in'..\Lib\UTIL\Util32.pas',
112 LoopPnThr in'..\Packages\LoopPnThr.pas';
113 114 {$R *.RES}115 116 begin117 ifnot IsPrevInst then118 begin119 Application.Initialize;
120 Application.CreateForm(TMainForm, MainForm);
121 Application.Run;
122 end123 else124 Application.Terminate;
125 end.
126 //Here's the other way...127 program RxProto;
128 129 uses130 Forms,
131 Main in'Main.pas'{MainForm},
132 Proc in'Proc.pas',
133 UTIL32 in'..\Lib\UTIL\Util32.pas',
134 LoopPnThr in'..\Packages\LoopPnThr.pas';
135 136 {$R *.RES}137 138 begin139 CheckPrevInstEx('TApplication', 'My Application');
140 //This code won't do anything if CheckPrevInstEx doesn't141 //pass muster142 Application.Initialize;
143 Application.CreateForm(TMainForm, MainForm);
144 Application.Run;
145 end.
As you can see, pretty simple stuff. Have fun with it!