在服务中使用内存映射

问题描述 投票:0回答:1

我构建了一个应用程序,该应用程序也可以作为服务运行(使用-service)开关。当我从命令提示符运行服务时,这完美地工作了,没有任何问题(我进行了一些设置,可以在不作为真正的服务运行时从控制台进行调试)。但是,当我尝试将其作为真正的服务运行,然后使用我的应用程序打开现有的内存映射时,出现错误...

无法找到指定的文件。

我如何将其作为服务或在控制台中运行:

[STAThread]
static void Main(string[] args)
{
    //Convert all arguments to lower
    args = Array.ConvertAll(args, e => e.ToLower());

    //Create the container object for the settings to be stored
    Settings.Bag = new SettingsBag();

    //Check if we want to run this as a service
    bool runAsService = args.Contains("-service");

    //Check if debugging
    bool debug = Environment.UserInteractive;

    //Catch all unhandled exceptions as well
    if (!debug || debug)
    {
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    }

    if (runAsService)
    {
        //Create service array
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new CRSService()
        };

        //Run services in interactive mode if needed
        if (debug)
            RunInteractive(ServicesToRun);
        else
            ServiceBase.Run(ServicesToRun);
    }
    else
    {
        //Start the main gui
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainGUI());
    }
}

在我的应用程序中,我有一个服务端和一个应用程序端。该应用程序的目的仅仅是控制服务。我使用内存映射文件进行所有控制,它似乎很好用,很适合我的需求。但是,当我将应用程序作为真正的服务运行时,我从调试日志中看到它正在使用正确的名称和访问设置创建内存映射文件。我还可以看到该文件在应有的位置被创建。服务中的所有工作似乎都与通过控制台调试时的工作完全相同。但是,我的应用程序(当作为应用程序而不是服务运行时)告诉我找不到内存映射文件。我也将它扔错了文件名路径,所以我知道它在正确的位置。

我如何打开内存映射(引发错误的位置):

m_mmf = MemoryMappedFile.OpenExisting(
    m_sMapName,
    MemoryMappedFileRights.ReadWrite
);

注意:该服务与运行Visual Studio所使用的帐户相同。作为示例,下图显示了我的任务管理器,services.msc gui和我当前标识的帐户。

enter image description here

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

c# shared-memory memory-mapped-files memory-mapping
1个回答
0
投票

Windows服务在会话0中独立运行,而您的控制台应用程序在用户会话中运行,因此为了使它们彼此通信,必须在Global\命名空间中创建内存映射文件才能使其其他会话可以访问。例如

var file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", ...

您还应该为文件设置适当的权限,以确保所有用户都可以访问它。

我建议阅读这篇文章Implementing Non-Persisted Memory Mapped Files Exposing IPC Style Communications with Windows Services,它对上述内容进行了更详细的说明,并提供了有关设置权限的示例,等等。


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

Mutex,互斥量安全性和MMF安全性策略创建

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");

创建并写入MMF

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();
    }

从MMF中读取

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);
       }
© www.soinside.com 2019 - 2024. All rights reserved.