There is a design pattern called Singleton that usually applies only to classes. But imho it can also be applied to applications to prevent the user from starting several instances of the application. I came along this article on DelphiAboutCom which shows how to create a single instance application. Unfortunately it doesn’t work.
What is going on?
If you run this application a second time it seems to work. But if you close all applications and then try to rerun it you have a problem because Atoms are not deleted automatically. You have to call GlobalDeleteAtom for every GlobalAddAtom. MSDN on GlobalAddAtom reads:
Global atoms are not deleted automatically when the application terminates. For every call to the GlobalAddAtom function, there must be a corresponding call to the GlobalDeleteAtom function.
So you may think that the code is correct if you add a GlobalDeleteAtom. Well, if you also add a try/finally block then you are correct.
var hAtom : THandle;
begin
if 0 <> GlobalFindAtom('xx')
ExitWithAMessage;
hAtom := GlobalAddAtom('xx');
try
Application.CreateForm(TFormMain, FormMain) ;
Application.Run;
finally
GlobalDeleteAtom(hAtom);
end;
(I omitted error checks for readability)
This looks good. However you should know that Atoms are not session (=Terminal Session = Logonuser session) wide! Each Windowstation (like the famous “Winsta0″) has its own global atom table. So this check fails if you run your application in another Windowstation. This case is very rare because other Windowstations do not have access to the local keyboard and graphic interface. So only applications with a remote control and without GUI can run there. But if you ever come into this situation do not use Atoms!
Instead use Mutexes:
Using Mutexes is an easier and more modern way than the atoms from 16-bit era. We have a really good advantage here:
Windows removes the mutex if there is no open handle anymore available. So even if you do not close the handle yourself – and you don’t need to do it because Windows does it on exit – the mutex will be destroyed and a second run of your application will succeed.
uses
Windows,
SyncObjs,
SysUtils;
var
SingleAppMutex : THandle;
begin
SingleAppMutex := CreateMutex(nil, True, PChar('{1CF7FD3B-4225-400A-B668-118FAD8A483B}'));
if GetLastError() = ERROR_ALREADY_EXISTS then
exit;
As you can see, I’m using a GUID to create a named mutex. This is very handy because it is very unlikely that another application uses such a name (even if it is a GUID) by accident.
You can generate a GUID from within Delphi if you hit Ctrl+Shift+G.
3 Responses
Cd-MaN
27|Oct|2009 1Shouldn’t the name in the CreateMutex call include the “Global\” prefix to ensure that it is truly systemwide?
OT: do you know a way to obtain the free TurboDelphi suite? The official line seems to be to “use the 30 day evaluation instead”
Christian Wimmer
27|Oct|2009 2Global\ prefix needs the SE_CREATE_GLOBAL_NAME TEXT(”SeCreateGlobalPrivilege”) privilege to create a global mutex. User programs can only check for session wide mutexes.
OT: Now, I don’t know. Sry.
Philip
14|Nov|2009 3Is SE_CREATE_GLOBAL_NAME really required for a Mutex? Reading http://msdn.microsoft.com/en-us/library/aa382954%28VS.85%29.aspx suggests that it is only needed for global MMFs. I haven’t tested it, though.
Leave a reply