/// <summary> /// Constructor. Specify whether the mutex it to be owned initially /// by the caller as well as the mutex's name. Note that with this /// constructor, the mutex will not be owned initially by the caller. /// </summary> /// <param name="name">Name of the mutex.</param> public GlobalMutex(string name) { SecurityAttributes sa = new SecurityAttributes(SecurityAccess.Unrestricted); 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."); } this.name = name; 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. string mutexName = @"Global\LT:MX:" + name; int error; retry: // Here's what the name abbreviations mean: // // LT = LillTek // MX = Mutex hMutex = WinApi.CreateMutex(sa.AttributesPtr, false, mutexName); error = WinApi.GetLastError(); if (hMutex == IntPtr.Zero) { if (mutexName.ToLowerInvariant().StartsWith(@"global\") && error == WinErr.ERROR_ACCESS_DENIED) { mutexName = "LT:MX:" + name; goto retry; } throw new Exception("Win32 mutex creation failed."); } } finally { sa.Close(); } }
/// <summary> /// This constructor initializes the event. /// </summary> /// <param name="name"> /// The name of the event. The name is limited to MAX_PATH characters and /// is case sensitive. May be passed as null. /// </param> /// <param name="requestedState">The requested initial event state: true for signalled.</param> /// <param name="actualState">This will be set to the actual state of the created event.</param> public GlobalManualResetEvent(string name, bool requestedState, out bool actualState) { SafeWaitHandle hEvent; SecurityAttributes sa; int err; if (name != null) { 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."); } } sa = new SecurityAttributes(SecurityAccess.Unrestricted); actualState = false; try { // Here's what the name abbreviations mean: // // LT = LillTek // EV = Event if (name != null) { name = @"Global\LT:EV:" + name; } hEvent = WinApi.CreateEvent(sa.AttributesPtr, true, requestedState, name); err = WinApi.GetLastError(); if (hEvent.IsInvalid) { throw new Exception("Unable to create the global event."); } if (requestedState) { actualState = err != WinApi.ERROR_ALREADY_EXISTS; } } finally { sa.Close(); } this.name = name; this.evt = new ManualResetEvent(actualState); this.evt.SafeWaitHandle = hEvent; }
/// <summary> /// Constructor. Specify whether the mutex it to be owned initially /// by the caller as well as the mutex's name. /// </summary> /// <param name="requestedState">The requsted initial state of the mutex (<c>true</c> if this is to be owned by the caller).</param> /// <param name="name">Name of the mutex.</param> /// <param name="actualState">Set to <c>true</c> if ownership of the mutex was granted to the caller.</param> public GlobalMutex(string name, bool requestedState, out bool actualState) { SecurityAttributes sa = new SecurityAttributes(SecurityAccess.Unrestricted); int err; 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."); } this.name = name; actualState = false; try { // Here's what the name abbreviations mean: // // LT = LillTek // EV = Event hMutex = WinApi.CreateMutex(sa.AttributesPtr, requestedState, @"Global\LT:MX:" + name); err = WinApi.GetLastError(); if (hMutex == IntPtr.Zero) { throw new Exception("Win32 mutex creation failed."); } if (requestedState) { actualState = err != WinApi.ERROR_ALREADY_EXISTS; } } finally { sa.Close(); } }
/// <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; } } } }
/// <summary> /// This constructor initializes the event. Note that this constructor /// will always return an event in the reset state. /// </summary> /// <param name="name"> /// The name of the event. The name is limited to 128 characters and /// is case sensitive. May be passed as null. /// </param> public GlobalManualResetEvent(string name) { SafeWaitHandle hEvent; SecurityAttributes sa; if (name != null) { 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."); } } sa = new SecurityAttributes(SecurityAccess.Unrestricted); try { // Here's what the name abbreviations mean: // // LT = LillTek // EV = Event if (name != null) { name = @"Global\LT:EV:" + name; } // $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 merfetryory 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: hEvent = WinApi.CreateEvent(sa.AttributesPtr, true, false, name); if (hEvent.IsInvalid) { if (name.ToLowerInvariant().StartsWith(@"global\") && WinApi.GetLastError() == WinErr.ERROR_ACCESS_DENIED) { name = name.Substring(7); // @"global\".Length goto retry; } throw new Exception("Unable to create the global event."); } } finally { sa.Close(); } this.name = name; this.evt = new ManualResetEvent(false); this.evt.SafeWaitHandle = hEvent; }