/// <summary> /// Default constructor. /// </summary> public SharedMem() { m_hMap = IntPtr.Zero; m_pvBlock = null; m_cLock = 0; m_cbBlock = 0; m_mutex = null; }
/// <summary> /// This method creates a shared memory object with the name passed in pszName /// and size of cbMem. The method returns <c>true</c> if the memory was created /// successfully, <c>false</c> if the operation failed for some reason. If the shared /// memory block is being created for the first time then this method will /// zero the memory block. If the shared memory block has already been created /// then this method will link to the existing block. Note that any successfull /// call to Open() needs to be matched by a call to <see cref="Close" />. This method is /// threadsafe. /// </summary> /// <param name="name"> /// Name of the inbox. This can be a maximum of 128 characters and may /// not include the backslash (\) character. /// </param> /// <param name="cbMem">Size of the memory in bytes.</param> /// <param name="mode">The opening mode.</param> public void Open(string name, int cbMem, OpenMode mode) { string memName; string mutexName; bool fExists; bool createdNew; if (name.Length > 128) { throw new ArgumentException("Name exceeds 128 characters.", "name"); } if (name.IndexOfAny(new char[] { '/', '\\' }) != -1) { throw new ArgumentException("Name may not include forward or backslashes."); } lock (syncLock) { Assertion.Test(m_hMap == IntPtr.Zero); // Here's what the abbreviations mean: // // LT = LillTek // SM = SharedMem memName = @"Global\LT:SM:" + name; #if SHAREDMEM_DRIVER bool fCreated; m_hMap = WinApi.MEM_Open(memName, cbMem, out fCreated); fExists = !fCreated; #else // Create the memory object SecurityAttributes sa; sa = new SecurityAttributes(SecurityAccess.Unrestricted); try { // $hack(jeff.lill): // // Beginning with a late service Windows XP service pack, applications not running in // Windows session 0 as a service cannot create global shared memory or other objects. // This results in the API below failing by returning a NULL handle and GetLastError() // returning ERROR_ACCESS_DENIED. Windows added this restriction to prevent malicious // code from creating global objects that will be used by well known services and then // squating on them. // // The work-around below detects this situation and tries creating a non-global object // instead. This will work for most unit testing scenarios. retry: m_hMap = WinApi.CreateFileMapping(new IntPtr(-1), sa.AttributesPtr, WinApi.PAGE_READWRITE, 0, (uint)cbMem, memName); if (m_hMap == IntPtr.Zero) { int error = WinApi.GetLastError(); if (memName.ToLowerInvariant().StartsWith(@"global\") && error == WinErr.ERROR_ACCESS_DENIED) { memName = "LT:SM:" + name; goto retry; } throw new InvalidOperationException(string.Format(null, "Failed on Windows error [{0}].", error)); } // For some weird reason, Marshal.GetLastWin32Error() is returning ERROR_IO_PENDING // when CreateFileMapping() is called on an exising block of shared memory instead // of returning ERROR_ALREADY_EXISTS. So I'm going to call GetLastError() directly. // This will be a bit of a performance hit but Open() will be called infrequently // in real applications. fExists = WinApi.GetLastError() == WinApi.ERROR_ALREADY_EXISTS; } finally { sa.Close(); } #endif // SHAREDMEM_DRIVER m_pvBlock = null; m_cbBlock = cbMem; m_cLock = 0; if (!fExists && mode == OpenMode.OPEN_ONLY) { #if SHAREDMEM_DRIVER WinApi.MEM_Close(m_hMap); #else WinApi.CloseHandle(m_hMap); #endif m_hMap = IntPtr.Zero; throw new InvalidOperationException("Shared memory does not exist."); } else if (fExists && mode == OpenMode.CREATE_ONLY) { #if SHAREDMEM_DRIVER WinApi.MEM_Close(m_hMap); #else WinApi.CloseHandle(m_hMap); #endif m_hMap = IntPtr.Zero; throw new InvalidOperationException("Shared memory already exists."); } // Here's what the abbreviations mean: // // LT = LillTek // SM = Shared Memory // MX = Mutex mutexName = "LT:SM:MX:" + name; if (fExists) { try { // Open the mutex m_mutex = new GlobalMutex(mutexName); } catch { #if SHAREDMEM_DRIVER WinApi.MEM_Close(m_hMap); #else WinApi.CloseHandle(m_hMap); #endif m_hMap = IntPtr.Zero; throw; } } else { try { // Create the mutex m_mutex = new GlobalMutex(mutexName, true, out createdNew); if (createdNew) { // Map the shared memory and zero it. byte *p; p = Lock(); for (int i = 0; i < m_cbBlock; p[i++] = 0) { ; } Unlock(); m_mutex.ReleaseMutex(); } } catch { #if SHAREDMEM_DRIVER WinApi.MEM_Close(m_hMap); #else WinApi.CloseHandle(m_hMap); #endif m_hMap = IntPtr.Zero; throw; } } } }