7

I am trying to make my app run only once on a computer, My app needs to comunicate to a webservice so it is bad to let it run more than once, currently im using Mutex with this:

MyMsg := RegisterWindowMessage('My_Unique_App_Message_Name');
Mutex := CreateMutex(nil, True, 'My_Unique_Application_Mutex_Name'); 
if (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS) then
exit;

Currently this works to limit 1 instance of application per user, but my app is being used in a Windows Server Environment where there are 20+ users loged in at a time, so i need to strict it to only run once per server, what i am trying to do is to declare that Mutex as a Global mutex but i fail to do so when i do the next code it doesnt work at all.

 MyMsg := RegisterWindowMessage('My_Unique_App_Message_Name');
 Mutex := CreateMutex(nil, True, 'Global\My_Unique_Application_Mutex_Name'); 
 if (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS) then
 begin
 exit

So am i doing something wrong? is there any other reliable way of not letting a second instance of my app to run?

Filburt
  • 17,626
  • 12
  • 64
  • 115
Miguel Filatov
  • 163
  • 3
  • 9
  • 2
    I think you need to define what "doesn't work at all" means. Does `CreateMutex` return 0? If so, what does `GetLastError` return? Is it `ERROR_ACCESS_DENIED` perhaps? – David Heffernan Dec 18 '13 at 22:49
  • @SertacAkyuz: the original check is correct. If `CreateMutex()` fails, or the mutex already exists, exit the app. Your change would bypass the "already exists" check if the mutex handle is not 0, as `CreateMutex()` returns a handle to an existing mutex. Remember, Delphi short-circuits Boolean expressions by default. – Remy Lebeau Dec 18 '13 at 23:03
  • @Remy - Ok, it seems fine, thanks. Additionally I mixed or with and... Removed comment not to cause confusion.. – Sertac Akyuz Dec 18 '13 at 23:08
  • @DavidHeffernan Yes when i add the Global\ prefix the CreateMutex gives back 0, if i use it without Global\ it gives me back the handle correctly. – Miguel Filatov Dec 18 '13 at 23:29
  • And `GetLastError`? That's how you find out what went wrong. – David Heffernan Dec 18 '13 at 23:29
  • If im not mistaking it gives back 0 aswell, could be wrong. As i dont have delphi nor source files on my home PC installed, will double check that tomorrow morning. – Miguel Filatov Dec 18 '13 at 23:31
  • 1
    @MiguelFilatov: `GetLastError()` will not return 0 if `CreateMutex()` returns NULL. It will return why NULL was returned. – Remy Lebeau Dec 18 '13 at 23:35
  • 1
    Global mutex and registered message won't mix well, anyways. – Free Consulting Dec 19 '13 at 00:06
  • Ok my mutex is "Global\7F4CFDF0-24A3-4446-AA49-ACA2902929E5" when i create it the create mutex gives me 404, but i can still open the same app on another user of the same pc (i suppose yesterday was too tired and did something wrong so it didnt work at all) – Miguel Filatov Dec 19 '13 at 06:58
  • It is not enough to just create the mutex, you must aquire it, if the first instance acquired the mutex, all other instances will fail acquiring the mutex. I will try to mockup an example... – whosrdaddy Dec 19 '13 at 07:05
  • I think @whosrdaddy you were right at the moment its working with this code: sMutex = 'Global\7F4CFDF0-24A3-4446-AA49-ACA2902929E5'; Mutex := CreateMutex(nil, true, sMutex); and the problem was that before i was doing: Mutex := CreateMutex(nil, false, sMutex); – Miguel Filatov Dec 19 '13 at 07:14

1 Answers1

12

By default, lpMutexAttributes = nil created mutexes are accessible only by the user running the process. The mutex needs to be accessible to all users.

Null DACL security descriptor

Do this by using a security descriptor with a null DACL. Please find below the code that I use for single instance applications/services

//Creates a mutex to see if the program is already running.
function IsSingleInstance(MutexName : string; KeepMutex : boolean = true):boolean;
const MUTEX_GLOBAL = 'Global\'; //Prefix to explicitly create the object in the global or session namespace. I.e. both client app (local user) and service (system account)

var MutexHandel : THandle;
    SecurityDesc: TSecurityDescriptor;
    SecurityAttr: TSecurityAttributes;
    ErrCode : integer;
begin
  //  By default (lpMutexAttributes =nil) created mutexes are accessible only by
  //  the user running the process. We need our mutexes to be accessible to all
  //  users, so that the mutex detection can work across user sessions.
  //  I.e. both the current user account and the System (Service) account.
  //  To do this we use a security descriptor with a null DACL.
  InitializeSecurityDescriptor(@SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@SecurityDesc, True, nil, False);
  SecurityAttr.nLength:=SizeOf(SecurityAttr);
  SecurityAttr.lpSecurityDescriptor:=@SecurityDesc;
  SecurityAttr.bInheritHandle:=False;

  //  The mutex is created in the global name space which makes it possible to
  //  access across user sessions.
  MutexHandel := CreateMutex(@SecurityAttr, True, PChar(MUTEX_GLOBAL + MutexName));
  ErrCode := GetLastError;

  //  If the function fails, the return value is 0
  //  If the mutex is a named mutex and the object existed before this function
  //  call, the return value is a handle to the existing object, GetLastError
  //  returns ERROR_ALREADY_EXISTS.
  if {(MutexHandel = 0) or }(ErrCode = ERROR_ALREADY_EXISTS) then
  begin
    result := false;
    closeHandle(MutexHandel);
  end
  else
  begin
    //  Mutex object has not yet been created, meaning that no previous
    //  instance has been created.
    result := true;

    if not KeepMutex then
       CloseHandle(MutexHandel);
  end;

  // The Mutexhandle is not closed because we want it to exist during the
  // lifetime of the application. The system closes the handle automatically
  //when the process terminates.
end;
Lars
  • 1,428
  • 1
  • 13
  • 22
  • 1
    Thank you for the answer, the main question here what is the diference of using this or just declaring the mutex without security attributes? at the moment i have tested it without the security parameter like this `CreateMutex(nil, True, 'Global\mutex-name');` and its working, so just to understand what does the security parameter actually do. The program is not opened with Administrator privileges althrough all the users in the server have a administrator account – Miguel Filatov Dec 19 '13 at 09:48
  • I needed to add the security attributes for mutex detection between applications and Services. In Win XP, Win Server 2003, and earlier versions, all services run in Session 0 along with applications. In Win Vista, Win Server 2008, and later versions, the operating system isolates services in Session 0 and runs applications in other sessions. – Lars Dec 20 '13 at 00:58
  • Miguel, your question is justified. Based on your example I changed bInitialOwner=true and was able to detect my application if I had it running in a Service Wrapper (srvany.exe). This was not previously possible. (I have not tested for other “user” cases.) You just helped me make my routine more generic and robust, thanks! PS. I’ve modified my answer above from: CreateMutex(@SecurityAttr, false,…) To: CreateMutex(@SecurityAttr, true,…) – Lars Dec 20 '13 at 01:03
  • I am happy that it helped other people apart of me, anyway before i was doing it without ownership and it didnt work, and with taking ownership it works, will have to try the Security attributes becouse at the moment have tried it in Windows 7 / Windows Server 2008, but we have clients using windows xp/2003, and about the service part need to try it aswell as in some cases we use the app as service with the same (srvany.exe), Thx for the reply! – Miguel Filatov Dec 20 '13 at 08:13
  • Can you please show this code in python aswell. Ive managed to create the mutex but I havn't been able to use the Null DACL Security Descriptor successfully. – yuval Jan 21 '16 at 14:51