Использование карт памяти с сервисом - PullRequest
8 голосов
/ 30 января 2020

Я создал приложение, которое также можно запускать как сервис (используя -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.ms c gui и моя текущая идентифицированная учетная запись.

enter image description here

Как я могу заставить свое клиентское приложение видеть файл карты памяти после того, как служба его создаст? Почему это работает, когда я запускаю его как консольную службу, а не когда я запускаю его как настоящую службу?

1 Ответ

5 голосов
/ 29 февраля 2020

Windows Службы выполняются изолированно, в сеансе 0, в то время как ваше консольное приложение выполняется в сеансе пользователя, поэтому для их взаимодействия друг с другом необходимо создать файл сопоставления памяти в пространстве имен Global\, сделать его доступным для других сессий. например,

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

Вы также должны установить соответствующие разрешения для файла, чтобы все пользователи могли получить к нему доступ.

Я бы порекомендовал прочитать это сообщение Реализация Файлы, сопоставленные с непостоянной памятью, раскрывающие связь в стиле IP C со Windows службами , которая более подробно объясняет вышеизложенное и содержит примеры по настройке разрешений, например, c.


Исходный код скопирован с поста, указанного выше:

Создание политики безопасности Mutex, Mutex Security и 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);
       }
...