Search code examples
c#asp.netiisasp.net-web-api2memory-mapped-files

How to commuicate with a Memory Mapped File from a Web API?


I have Web API with a controller that needs to communicate with a local tool via a memory mapped file. When I try to open such file with OpenExisting I get a "file not found" error.

string MMF_In_Name = "MemoryMappedFilename";
MemoryMappedFile MMF_In = MemoryMappedFile.OpenExisting(MMF_In_Name);

I tried add the prefix "Global/" to the name but without success.

Next I tried was to start a command line tool from my controller. The tool starts but it gets the same "file not found error". When I run the tool myself all works just fine. That means that the file name is correct.

How can I convince the IIS worker to let me open and use a memory mapped file?

I'm using Windows Server 2012 and ISS 8.5.


Solution

  • This works on Windows Server 2012 and IIS 8.5.

    It's important to understand that the IIS worker runs in a different Terminal Server Session than normal applications. Much like a Windows Service.

    So when the application exposing a Memory Mapped File it needs create it via the "Global\" prefix added to the name. But it also needs add a security descriptor or identity. In c# it would look like this:

    string MMF_Name = @"Global\MyMemoryMappedFileName";
    
    var security = new MemoryMappedFileSecurity();
    security.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MemoryMappedFileRights.FullControl, AccessControlType.Allow));
    
    
    var mmf = MemoryMappedFile.CreateOrOpen(MMF_Name
        , 1024 * 1024
        , MemoryMappedFileAccess.ReadWrite
        , MemoryMappedFileOptions.None
        , security
        , System.IO.HandleInheritability.Inheritable);
    

    In C++ it would look like this:

    TCHAR szName[] = TEXT("Global\MyMemoryMappedFileName");
    
    HANDLE hMapFile;
    LPCTSTR pBuf;
    
    SECURITY_DESCRIPTOR sd;
    
    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
      printf("InitializeSecurityDescriptor failed %d\n", GetLastError());
    
    if (!SetSecurityDescriptorDacl(&sd, true, 0, false))
      printf("SetSecurityDescriptorDacl failed %d\n", GetLastError());
    
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = false;
    
    
    
    hMapFile = CreateFileMapping(
      INVALID_HANDLE_VALUE,    // use paging file
      &sa,                    // default security
      PAGE_READWRITE,          // read/write access
      0,                       // maximum object size (high-order DWORD)
      BUF_SIZE,                // maximum object size (low-order DWORD)
      szName);                 // name of mapping object
    
    if (hMapFile == NULL)
    {
      _tprintf(TEXT("Could not create file mapping object (%d).\n"),
           GetLastError());
      return 1;
    }
    

    An application creating such objects needs to start with Admin rights.

    Now when a client like the IIS worker tries to access the file it needs to make sure to use the correct name, aka use the "Global\" prefix. In C# it would look like:

    string MMF_Name = @"Global\MyMemoryMappedFileName";
    
    var MMF = MemoryMappedFile.OpenExisting(MMF_Name
        , MemoryMappedFileRights.ReadWrite
        , HandleInheritability.Inheritable);
    

    In C++:

    TCHAR szName[] = TEXT("Global\\MyMemoryMappedFileName");
    
    HANDLE hMapFile;
    LPCTSTR pBuf;
    
    hMapFile = OpenFileMapping(
      FILE_MAP_ALL_ACCESS,   // read/write access
      TRUE,                 // !!!!! do inherit the name
      szName);               // name of mapping object
    
    if (hMapFile == NULL)
    {
      _tprintf(TEXT("Could not open file mapping object (%d).\n"),
           GetLastError());
      return 1;
    }
    

    When all this is done. The IIS worker should be able to access the application via the memory mapped file. No need to change the identity of the worker. In fact, I run it in default settings.