Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using memory maps with a service

I built an application that can also be ran as a service (using a -service) switch. This works perfectly with no issues when I'm running the service from a command prompt (I have something set up that lets me debug it from a console when not being ran as a true service). However, when I try to run it as a true service then use my application to open the existing memory map, I get the error...

Unable to find the specified file.

How I run it as a service or in console:

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

In my application I have a service side and an application side. The application's purpose is just to control the service. I do all the controlling using memory mapping files and it seems to work great and suits my needs. However, when I run the application as a true service I see from my debug logs it is creating the memory map file with the correct name and access settings. I can also see the file getting created where it should be. Everything seems to working exactly the same in the service as it does when I debug via console. However, my application (when ran as an application instead of the service) tells me it can not find the memory map file. I have it toss the file name path in the error as well so I know it's looking in the right place.

How I open the memory map (where the error is thrown):

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

Note: The service is running as the same account as I run Visual Studio in. As an example the image below shows my task manager, the services.msc gui and my currently identified account.

enter image description here

How can I get my client application to see the memory map file after the service creates it? Why does it work when I run it as a console service and not when I run it as a true service?

like image 808
Arvo Bowen Avatar asked Jan 30 '20 00:01

Arvo Bowen


People also ask

What is the purpose of a Memory-Map and how is it used?

When a computer first boots up (starts), the memory map tells the OS how much memory is available. As the computer runs, the memory map ensures that data is always written to, and read from, the proper places. The memory map also ensures that the computer's debuggers can resolve memory addresses to actual stored data.

Where are Memory-Map files stored?

Memory-mapped files are accessed through the operating system's memory manager, so the file is automatically partitioned into a number of pages and accessed as needed. You do not have to handle the memory management yourself.

How do you do memory mapping?

Step 2 − Map the file contents into memory using mmap() system call. This would return the start address after mapped into the memory. Step 3 − Access the file contents using array notation (can also access with pointer notation) as doesn't read expensive read() system call.

Is Memory-Map software free?

Memory-Map includes free access to small scale topographic maps and many other free maps around the world. More detailed maps and nautical charts are available for download and purchase with a free try-before-you-buy, time-limited demo option.


1 Answers

Windows Services run in isolation, in Session 0, whilst your Console application runs in a user session, so for them to communicate with each other, the memory mapped file must be created in the Global\ namespace, to make it accessible to other sessions. e.g.

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

You should also set the appropriate permissions to the file, to make sure all users can access it.

I'd recommend reading this post Implementing Non-Persisted Memory Mapped Files Exposing IPC Style Communications with Windows Services, which explains the above in a lot more detail and has examples on setting the permissions, etc.


Source code copied from the post linked above:

Mutex, Mutex Security & MMF Security Policy Creation

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

Create & Write to the 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();
    }

Reading from the 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);
       }
like image 57
C. Augusto Proiete Avatar answered Oct 01 '22 22:10

C. Augusto Proiete