My program uses Inno Setup to install/uninstall it. In my application code I create a Global mutex using the CreateMutex
Windows API function. Then in my Inno Setup program I have the following code:
AppMutex=Global\MyProgramMutex.2A23834B-2919-4007-8C0A-3C7EDCA7186E
function InitializeSetup(): Boolean;
begin
Result := True;
if (CreateMutex(0, False, '{#SetupSetting('AppId')}') <> 0) and (DLLGetLastError = ERROR_ALREADY_EXISTS) then
begin
Result := False;
MsgBox('Another instance of the Setup program is already running. Please close it and try again', mbCriticalError, MB_OK);
end;
if CheckForMutexes('{#SetupSetting('AppMutex')}') then
begin
Result := False;
MsgBox('{#SetupSetting('AppName')} ' + 'appears to be running. Please close all instances of the program before continuing.', mbCriticalError, MB_OK);
end;
end;
This works great, as expected, for the user running the Inno Setup program. The question/problem I have is: If I "Switch User" and start the application as a different user, and then switch back to the original user, the Setup program does not detect that the application is running under a different user.
I'm not knowledgeable all round enough to know, if the Setup program can detect the running application.
As documented, in Inno Setup FAQ Detecting instances running in any user session with AppMutex:
To detect mutexes created in other sessions, your application must create two mutexes: one with a
Global\
prefix and the other without.Mutexes with the
Global\
prefix are accessible from any user session. A like-named mutex must also be created in the session namespace (i.e. without theGlobal\
prefix) in case the creation of the Global mutex failed due to security restrictions.Additionally, a special security descriptor must be passed in each of the CreateMutex() calls to ensure the mutex is accessible by different users.
To make a mutex accessible by all users in C#, see:
What is a good pattern for using a Global Mutex in C#?
In sum, the code in your C# application should be like (you need System.Threading.AccessControl
NuGet package):
const string mutexId = "MyProg";
var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var allowEveryoneRule =
new MutexAccessRule(
everyone, MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
Mutex globalMutex = null;
try
{
const string globalMutexId = $@"Global\{mutexId}";
globalMutex =
MutexAcl.Create(false, globalMutexId, out bool _, securitySettings);
}
catch (UnauthorizedAccessException)
{
// Ignore
}
Mutex localMutex = new Mutex(false, mutexId);
try
{
// Run your program here
}
finally
{
// These have to be called only after the application (its windows) closes.
// You can also remove these calls and let the system release the mutexes.
if (globalMutex != null)
{
globalMutex.Dispose();
}
localMutex.Dispose();
}
On Inno Setup side, all you need is to list both mutexes in the AppMutex
directive:
[Setup]
AppMutex=MyProg,Global\MyProg
You do not need your CreateMutex
and CheckForMutexes
calls in the InitializeSetup
funcion.