RETierney RETierney - 29 days ago 13
C# Question

Tell if any logged on user is running the application about to be installed/uninstalled

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.

Answer

As documented, in Inno Setup KB Detect 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 the Global\ prefix) in case the creation of the Global mutex failed due to security restrictions or lack of operating system support (versions of Windows NT prior to 4.0 Terminal Server Edition don't support the Global\ prefix).

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:

const string mutexId = "MyProg";
MutexAccessRule allowEveryoneRule =
    new MutexAccessRule(
        new SecurityIdentifier(WellKnownSidType.WorldSid, null),
        MutexRights.FullControl, AccessControlType.Allow);
MutexSecurity securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);

Mutex globalMutex = null;

try
{
    bool createdNew;
    globalMutex = new Mutex(false, "Global\\" + mutexId, out createdNew, 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.