C# 在服务中使用内存映射

C# 在服务中使用内存映射,c#,shared-memory,memory-mapped-files,memory-mapping,C#,Shared Memory,Memory Mapped Files,Memory Mapping,我构建了一个也可以作为服务运行的应用程序(使用-service)开关。当我在命令提示符下运行服务时,这一点非常好,没有任何问题(我已经设置了一些设置,允许我在不作为真正的服务运行时从控制台对其进行调试)。但是,当我尝试将其作为真正的服务运行,然后使用我的应用程序打开现有的内存映射时,我得到了错误 找不到指定的文件 如何将其作为服务或在控制台中运行: 在我的应用程序中,我有一个服务端和一个应用程序端。应用程序的目的只是控制服务。我使用内存映射文件进行所有控制,它似乎工作得很好,适合我的需要。但是,

我构建了一个也可以作为服务运行的应用程序(使用
-service
)开关。当我在命令提示符下运行服务时,这一点非常好,没有任何问题(我已经设置了一些设置,允许我在不作为真正的服务运行时从控制台对其进行调试)。但是,当我尝试将其作为真正的服务运行,然后使用我的应用程序打开现有的内存映射时,我得到了错误

找不到指定的文件

如何将其作为服务或在控制台中运行: 在我的应用程序中,我有一个服务端和一个应用程序端。应用程序的目的只是控制服务。我使用内存映射文件进行所有控制,它似乎工作得很好,适合我的需要。但是,当我将应用程序作为真正的服务运行时,我从调试日志中看到它正在使用正确的名称和访问设置创建内存映射文件。我还可以看到文件在应该创建的地方被创建。当我通过控制台进行调试时,服务中的一切似乎都完全一样。但是,我的应用程序(当作为应用程序而不是服务运行时)告诉我它找不到内存映射文件。我让它在错误中抛出文件名路径,所以我知道它在正确的位置

如何打开内存映射(引发错误的位置): 注意:该服务的运行帐户与我在中运行Visual Studio的帐户相同。作为示例,下图显示了我的任务管理器、services.msc gui和我当前识别的帐户


在服务创建内存映射文件后,如何让客户端应用程序看到它?为什么当我将其作为控制台服务运行时,它工作,而当我将其作为真正的服务运行时,它不工作?

Windows服务在会话
0
中独立运行,而您的控制台应用程序在用户会话中运行,因此为了使它们相互通信,必须在
全局\
命名空间中创建内存映射文件,使其可供其他会话访问。e、 g

var file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", ...
您还应该为该文件设置适当的权限,以确保所有用户都可以访问该文件

我建议您阅读这篇文章,它详细地解释了上述内容,并提供了设置权限等示例


从上面链接的帖子复制的源代码:

互斥、互斥安全和MMF安全策略创建
bool mutexCreated;
互斥互斥;
MutexSecurity MutexSecurity=新的MutexSecurity();
MemoryMappedFileSecurity mmfSecurity=新的MemoryMappedFileSecurity();
mutexSecurity.AddAccessRule(新的MutexAccessRule(新的SecurityIdentifier(WellKnownSidType.WorldSid,null)),
MutexRights.Synchronize | MutexRights.Modify,AccessControlType.Allow));
mmfSecurity.AddAccessRule(新的AccessRule(“每个人”),MemoryMappedFileRights.FullControl,
AccessControlType.Allow);
mutex=新的互斥体(false,@“Global\MyMutex”,out mutexCreated,mutexSecurity);
if(mutexCreated==false)log.DebugFormat(“创建互斥体时出错”);
else log.DebugFormat(“已成功创建互斥体”);
创建并写入MMF
MemoryMappedFile=MemoryMappedFile.CreateOrOpen(@“Global\MyMemoryMappedFile”,4096,
MemoryMappedFileAccess.ReadWrite、MemoryMappedFileOptions.DelayAllocatePages、MMF安全、,
可继承性);
使用(MemoryMappedViewAccessor=file.CreateViewAccessor()){
字符串xmlData=SerializeToXml(CurrentJobQueue)+“\0”;//\0终止XML以停止格式错误
当写入的下一个字符串短于当前字符串时出现问题
字节[]缓冲区=ConvertStringToByteArray(xmlData);
mutex.WaitOne();
accessor.WriteArray(0,buffer,0,buffer.Length);
mutex.ReleaseMutex();
}
从MMF中读取
使用(MemoryMappedFile=MemoryMappedFile.OpenExisting(
@“全局\MyMemoryMappedFile”,MemoryMappedFiles.Read){
使用(MemoryMappedViewAccessor)存取器=
CreateViewAccessor(0,0,MemoryMappedFileAccess.Read)){
字节[]缓冲区=新字节[accessor.Capacity];
Mutex Mutex=Mutex.OpenExisting(@“Global\MyMutex”);
mutex.WaitOne();
ReadArray(0,buffer,0,buffer.Length);
mutex.ReleaseMutex();
字符串xmlData=convertbytearrayotstring(缓冲区);
数据=从XML(xmlData)反序列化;
}

@Amy我认为Visual Studio的版本很重要,因为我不能使用比VS2017更高的版本。例如,如果一个答案可以使用VS2019,但不能使用2017,那么答案将不会被接受。希望这是有意义的。“我不能使用比VS2017更高的版本”这不是怀疑VS有责任的理由。您使用的是什么版本的C#和.Net Framework?将它们添加到您的问题中。只有当问题是关于VS时,才应应用VS标记。服务注册在哪个帐户下运行?如果是系统帐户,则问题可能是句柄和名称受到保护,不受攻击访问它们的用户帐户。您是否在名称前加了全局前缀,如“Global\\MyName”?您可以从中看到这一点,这就是问题所在,它的名称必须是@“Global\myappsvr\u mm\u toggles”当您将其作为服务运行时,使其工作。服务在与登录用户会话不同的会话中运行,Windows将内核对象的名称空间保持为会话隔离,以便它们不会相互干扰。除非您选择使用全局名称空间。当您测试它时,它起作用,因为两个程序都在同一会话中运行。
m_mmf = MemoryMappedFile.OpenExisting(
    m_sMapName,
    MemoryMappedFileRights.ReadWrite
);
var file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", ...
bool mutexCreated;
Mutex mutex;
MutexSecurity mutexSecurity = new MutexSecurity();
MemoryMappedFileSecurity mmfSecurity = new MemoryMappedFileSecurity();

mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), 
MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
mmfSecurity.AddAccessRule(new AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, 
AccessControlType.Allow));

mutex = new Mutex(false, @"Global\MyMutex", out mutexCreated, mutexSecurity);
if (mutexCreated == false) log.DebugFormat("There has been an error creating the mutex"); 
else log.DebugFormat("mutex created successfully");
MemoryMappedFile file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", 4096, 
MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, mmfSecurity, 
HandleInheritability.Inheritable);

using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor()) {
   string xmlData = SerializeToXml(CurrentJobQueue) + "\0"; // \0 terminates the XML to stop badly formed 
issues when the next string written is shorter than the current

    byte[] buffer = ConvertStringToByteArray(xmlData);
    mutex.WaitOne();
    accessor.WriteArray<byte>(0, buffer, 0, buffer.Length);
    mutex.ReleaseMutex();
    }
using (MemoryMappedFile file = MemoryMappedFile.OpenExisting(
   @"Global\MyMemoryMappedFile", MemoryMappedFileRights.Read)) {

     using (MemoryMappedViewAccessor accessor =
         file.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read)) {
         byte[] buffer = new byte[accessor.Capacity];

         Mutex mutex = Mutex.OpenExisting(@"Global\MyMutex");
         mutex.WaitOne();
         accessor.ReadArray<byte>(0, buffer, 0, buffer.Length);
         mutex.ReleaseMutex();

         string xmlData = ConvertByteArrayToString(buffer);
         data = DeserializeFromXML(xmlData);
       }