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.
[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.
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.
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?
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.
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.
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.
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With