Permission Control with Mutex and MemoryMappedFile in .NET

Learn to use Mutex and MemoryMappedFile for .NET IPC. This guide covers setting security permissions, crucial for communication with Windows Services.

When implementing Inter-Process Communication (IPC) in .NET, Mutex and MemoryMappedFile are common and powerful tools. However, access issues can arise when one of the processes is a Windows Service due to permission differences. This article explains how to set permissions correctly and highlights a critical consideration when using a Mutex.

The Mutex and await Pitfall

A crucial rule when working with a Mutex is that the lock must be acquired and released on the same thread.

Mutex.WaitOne() blocks the current thread until the lock is acquired, and Mutex.ReleaseMutex() releases it. If you use the await keyword between WaitOne() and ReleaseMutex(), the code after the await may be executed by a different thread from the thread pool. If this happens, ReleaseMutex() will be called on a thread that does not own the lock, throwing an ApplicationException.

Warning: You must not perform an await operation between the Mutex.WaitOne() and Mutex.ReleaseMutex() methods.

Permission Control with Windows Services

Permission issues often occur during IPC between a standard desktop application and a Windows Service. This is because Windows Services typically run under accounts with different privileges, such as Local System, Local Service, or Network Service, while a desktop app runs under the currently logged-in user’s account.

To allow these two processes to share named kernel objects like a Mutex or MemoryMappedFile, you need to explicitly set Access Control Lists (ACLs) for these objects.

The following example demonstrates how to grant the current user full control over a global Mutex and MemoryMappedFile.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.IO.MemoryMappedFiles;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;

// Get the identity of the current user
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
string user = currentUser.Name;

// 1. Create or open a global Mutex and set its permissions
// The "Global\\" prefix makes the object visible across all terminal server sessions
var mutex = new Mutex(false, "Global\\mutex_ZhongRuanPMS");

// Create an access rule granting the current user full control
var maRule = new MutexAccessRule(user, MutexRights.FullControl, AccessControlType.Allow);

// Get the existing security settings and add the new rule
MutexSecurity mSec = mutex.GetAccessControl();
mSec.AddAccessRule(maRule);
mutex.SetAccessControl(mSec); // Apply the new security settings

// 2. Create or open a global MemoryMappedFile and set its permissions
var memoryMappedFile = MemoryMappedFile.CreateOrOpen("Global\\mmf_ZhongRuanPMS", 20480, MemoryMappedFileAccess.ReadWrite);

// Create a similar access rule for the memory-mapped file
var mmfRule = new AccessRule<MemoryMappedFileRights>(user, MemoryMappedFileRights.FullControl, AccessControlType.Allow);

// Get the existing security settings and add the new rule
MemoryMappedFileSecurity msc = memoryMappedFile.GetAccessControl();
msc.AddAccessRule(mmfRule);
memoryMappedFile.SetAccessControl(msc); // Apply the new security settings

By setting these permissions, both the application and the Windows Service can successfully access and synchronize with these shared kernel objects, even when running in different security contexts.

Built with Hugo
Theme Stack designed by Jimmy