How to create a memory mapped file with C# (Windows Vista / Windows 7)
Hi all,
The following C# sample shows how to create a memory mapped file and use private namespaces to allow access to specific groups of users:
FORM1.CS
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.IO;
namespace Alejacma
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
bool bResult = false;
IntPtr hBoundary = IntPtr.Zero;
IntPtr pSid = IntPtr.Zero;
int cbSid = Win32.SECURITY_MAX_SID_SIZE;
IntPtr hNamespace = IntPtr.Zero;
Win32.SECURITY_ATTRIBUTES securityAttributes = new Win32.SECURITY_ATTRIBUTES();
IntPtr hFile = IntPtr.Zero;
IntPtr pView = IntPtr.Zero;
IntPtr pData = IntPtr.Zero;
try
{
// Create boundary
hBoundary = Win32.CreateBoundaryDescriptor(
"AlejacmaBoundaryDescriptor",
0
);
if (hBoundary == IntPtr.Zero) { throw new Exception("CreateBoundaryDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }
pSid = Marshal.AllocHGlobal(cbSid);
bResult = Win32.CreateWellKnownSid(
WellKnownSidType.BuiltinAdministratorsSid,
IntPtr.Zero,
pSid,
ref cbSid
);
if (!bResult) { throw new Exception("CreateWellKnownSid", new Win32Exception(Marshal.GetLastWin32Error())); }
bResult = Win32.AddSIDToBoundaryDescriptor(
ref hBoundary,
pSid
);
if (!bResult) { throw new Exception("AddSIDToBoundaryDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }
// Create namespace and give access to some groups of users:
// - Remote Desktop Users (needed for remote users to access the mapped file)
// - Administrators (needed to create the mapped file within this program)
// - Interactive users (needed for local users to access mapped file)
bResult = Win32.ConvertStringSecurityDescriptorToSecurityDescriptor(
"D:(A;;GA;;;RD)(A;;GA;;;S-1-5-32-544)(A;;GA;;;S-1-5-4)",
Win32.SDDL_REVISION_1,
out securityAttributes.lpSecurityDescriptor,
IntPtr.Zero
);
if (!bResult) { throw new Exception("ConvertStringSecurityDescriptorToSecurityDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }
securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
securityAttributes.bInheritHandle = false;
hNamespace = Win32.CreatePrivateNamespace(
ref securityAttributes,
hBoundary,
"Alejacma"
);
if (hNamespace == IntPtr.Zero) { throw new Exception("CreatePrivateNamespace", new Win32Exception(Marshal.GetLastWin32Error())); }
// Create file mapping, and give access to all users with access to the namespace
hFile = Win32.CreateFileMapping(
Win32.INVALID_HANDLE_VALUE,
ref securityAttributes,
Win32.PAGE_READWRITE,
0,
20971520,
"Alejacma\\TestFileMapping"
);
if (hFile == IntPtr.Zero) { throw new Exception("CreateFileMapping", new Win32Exception(Marshal.GetLastWin32Error())); }
// Map file and write something to it
pView = Win32.MapViewOfFile(
hFile,
Win32.FILE_MAP_WRITE,
0,
0,
11
);
if (pView == IntPtr.Zero) { throw new Exception("MapViewOfFile", new Win32Exception(Marshal.GetLastWin32Error())); }
pData = Marshal.StringToHGlobalAnsi("Hello World");
Win32.MemCopy(pView, pData, 11);
// We are done. Now a reader app can access the data
MessageBox.Show("Run the reader app now!");
}
catch (Exception ex)
{
// Any error?
MessageBox.Show(ex.Message + " failed with '" + ex.InnerException.Message + "' error");
}
finally
{
// Clean up memory
if (pSid != IntPtr.Zero)
{
Marshal.FreeHGlobal(pSid);
}
if (securityAttributes.lpSecurityDescriptor != IntPtr.Zero)
{
Win32.LocalFree(securityAttributes.lpSecurityDescriptor);
}
if (pData != IntPtr.Zero)
{
Marshal.FreeHGlobal(pData);
}
// We must also close hBoundary, hNamespace, hFile and pView when we don't need them anymore.
// Check MSDN's description of the APIs we used to create them to figure out how to close them.
}
}
}
}
WIN32.CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace Alejacma
{
class Win32
{
public const int SECURITY_MAX_SID_SIZE = 68;
public const int SDDL_REVISION_1 = 1;
public const uint INVALID_HANDLE_VALUE = 0xffffffff;
public const int PAGE_READWRITE = 0x04;
public const int FILE_MAP_WRITE = 0X02;
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateBoundaryDescriptor
(
[In] string Name,
[In] int Flags
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CreateWellKnownSid
(
[In] WellKnownSidType WellKnownSidType,
[In] [Optional] IntPtr DomainSid,
[In] IntPtr pSid,
[In][Out]ref int cbSid
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AddSIDToBoundaryDescriptor
(
[In][Out] ref IntPtr BoundaryDescriptor,
[In] IntPtr RequiredSid
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor
(
[In] string StringSecurityDescriptor,
[In] int StringSDRevision,
[Out] out IntPtr SecurityDescriptor,
[Out] IntPtr SecurityDescriptorSize
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalFree([In] IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreatePrivateNamespace(
[In][Optional] ref SECURITY_ATTRIBUTES lpPrivateNamespaceAttributes,
[In] IntPtr lpBoundaryDescriptor,
[In] string lpAliasPrefix
);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateFileMapping(
[In] uint hFile,
[In][Optional] ref SECURITY_ATTRIBUTES lpAttributes,
[In] int flProtect,
[In] int dwMaximumSizeHigh,
[In] int dwMaximumSizeLow,
[In][Optional] string lpName
);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
[In] IntPtr hFileMappingObject,
[In] int dwDesiredAccess,
[In] int dwFileOffsetHigh,
[In] int dwFileOffsetLow,
[In] int dwNumberOfBytesToMap
);
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemCopy(IntPtr dest, IntPtr src, uint count);
}
}
I hope this helps.
Regards,
Alex (Alejandro Campos Magencio)