在 .NET 中进行进程间通信(IPC)时,Mutex
(互斥体)和 MemoryMappedFile
(内存映射文件)是常用的工具。然而,当其中一个进程是 Windows 服务时,由于权限差异,可能会遇到访问问题。本文将介绍如何正确设置权限,并强调一个使用 Mutex
时的重要注意事项。
Mutex
与 await
的陷阱
在使用 Mutex
时,一个至关重要的原则是:必须在同一个线程上获取和释放锁。
Mutex.WaitOne()
会阻塞当前线程,直到获取到锁。Mutex.ReleaseMutex()
则会释放锁。如果你在 WaitOne()
和 ReleaseMutex()
之间使用了 await
关键字,await
后面的代码可能会由线程池中的另一个不同线程执行。如果发生这种情况,ReleaseMutex()
将在一个未获取锁的线程上被调用,从而抛出 ApplicationException
。
警告: 在 Mutex.WaitOne()
和 Mutex.ReleaseMutex()
方法之间 绝对不能 执行 await
操作。
Windows 服务下的权限控制
当普通桌面应用程序与 Windows 服务之间进行 IPC 时,会遇到权限问题。这是因为 Windows 服务通常在 Local System
、Local Service
或 Network Service
等具有不同权限的账户下运行,而桌面应用则在当前登录用户的账户下运行。
为了让这两个进程能够共享 Mutex
和 MemoryMappedFile
等命名的内核对象,你需要为这些对象明确设置访问控制规则(ACL)。
下面的示例代码展示了如何为当前用户授予对全局 Mutex
和 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;
// 获取当前用户的身份信息
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
string user = currentUser.Name;
// 1. 创建或打开一个全局 Mutex 并设置权限
// "Global\\" 前缀确保对象在所有终端服务会话中可见
var mutex = new Mutex(false, "Global\\mutex_ZhongRuanPMS");
// 创建一个访问规则,授予当前用户完全控制权限
var maRule = new MutexAccessRule(user, MutexRights.FullControl, AccessControlType.Allow);
// 获取现有的安全设置并添加新的规则
MutexSecurity mSec = mutex.GetAccessControl();
mSec.AddAccessRule(maRule);
mutex.SetAccessControl(mSec); // 应用新的安全设置
// 2. 创建或打开一个全局内存映射文件并设置权限
var memoryMappedFile = MemoryMappedFile.CreateOrOpen("Global\\mmf_ZhongRuanPMS", 20480, MemoryMappedFileAccess.ReadWrite);
// 为内存映射文件创建类似的访问规则
var mmfRule = new AccessRule<MemoryMappedFileRights>(user, MemoryMappedFileRights.FullControl, AccessControlType.Allow);
// 获取现有的安全设置并添加新的规则
MemoryMappedFileSecurity msc = memoryMappedFile.GetAccessControl();
msc.AddAccessRule(mmfRule);
memoryMappedFile.SetAccessControl(msc); // 应用新的安全设置
|
通过这种方式,即使应用程序和 Windows 服务在不同的安全上下文中运行,它们也可以成功地访问和同步这些共享的内核对象。