/src/SystemLayer/SingleInstaceManager.cs
C# | 349 lines | 302 code | 35 blank | 12 comment | 25 complexity | 831d4dbcf679aa47a7cb54ca2c77b24b MD5 | raw file
- /////////////////////////////////////////////////////////////////////////////////
- // Paint.NET //
- // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
- // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
- // See src/Resources/Files/License.txt for full licensing and attribution //
- // details. //
- // . //
- /////////////////////////////////////////////////////////////////////////////////
-
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Runtime.InteropServices;
- using System.Windows.Forms;
-
- namespace PaintDotNet.SystemLayer
- {
- /// <summary>
- /// Provides a way to manage and communicate between instances of an application
- /// in the same user session.
- /// </summary>
- public sealed class SingleInstanceManager
- : IDisposable
- {
- private const int MappingSize = 8; // sizeof(int64)
- private readonly string _mappingName;
- private Form _window;
- private IntPtr _hWnd = IntPtr.Zero;
- private IntPtr _hFileMapping;
- private readonly List<string> _pendingInstanceMessages = new List<string>();
- private readonly bool _isFirstInstance;
-
- public bool IsFirstInstance
- {
- get
- {
- return _isFirstInstance;
- }
- }
-
- public bool AreMessagesPending
- {
- get
- {
- lock (_pendingInstanceMessages)
- {
- return (_pendingInstanceMessages.Count > 0);
- }
- }
- }
-
- public void SetWindow(Form newWindow)
- {
- if (_window != null)
- {
- UnregisterWindow();
- }
-
- RegisterWindow(newWindow);
- }
-
- private void UnregisterWindow()
- {
- if (_window == null) return;
- _window.HandleCreated -= WindowHandleCreated;
- _window.HandleDestroyed -= WindowHandleDestroyed;
- _window.Disposed -= WindowDisposed;
- WriteHandleValueToMappedFile(IntPtr.Zero);
- _hWnd = IntPtr.Zero;
- _window = null;
- }
-
- private void RegisterWindow(Form newWindow)
- {
- _window = newWindow;
-
- if (_window != null)
- {
- _window.HandleCreated += WindowHandleCreated;
- _window.HandleDestroyed += WindowHandleDestroyed;
- _window.Disposed += WindowDisposed;
-
- if (_window.IsHandleCreated)
- {
- _hWnd = _window.Handle;
- WriteHandleValueToMappedFile(_hWnd);
- }
- }
-
- GC.KeepAlive(newWindow);
- }
-
- private void WindowDisposed(object sender, EventArgs e)
- {
- UnregisterWindow();
- }
-
- private void WindowHandleDestroyed(object sender, EventArgs e)
- {
- UnregisterWindow();
- }
-
- private void WindowHandleCreated(object sender, EventArgs e)
- {
- _hWnd = _window.Handle;
- WriteHandleValueToMappedFile(_hWnd);
- GC.KeepAlive(_window);
- }
-
- public string[] GetPendingInstanceMessages()
- {
- string[] messages;
-
- lock (_pendingInstanceMessages)
- {
- messages = _pendingInstanceMessages.ToArray();
- _pendingInstanceMessages.Clear();
- }
-
- return messages;
- }
-
- public event EventHandler InstanceMessageReceived;
- private void OnInstanceMessageReceived()
- {
- if (InstanceMessageReceived != null)
- {
- InstanceMessageReceived(this, EventArgs.Empty);
- }
- }
-
- public void SendInstanceMessage(string text)
- {
- SendInstanceMessage(text, 1);
- }
-
- public void SendInstanceMessage(string text, int timeoutSeconds)
- {
- IntPtr ourHwnd = IntPtr.Zero;
- DateTime now = DateTime.Now;
- DateTime timeoutTime = DateTime.Now + new TimeSpan(0, 0, 0, timeoutSeconds);
-
- while (ourHwnd == IntPtr.Zero && now < timeoutTime)
- {
- ourHwnd = ReadHandleFromFromMappedFile();
- now = DateTime.Now;
-
- if (ourHwnd == IntPtr.Zero)
- {
- System.Threading.Thread.Sleep(100);
- }
- }
-
- if (ourHwnd == IntPtr.Zero)
- {
- }
- else
- {
- var copyDataStruct = new NativeStructs.COPYDATASTRUCT();
- IntPtr szText = IntPtr.Zero;
-
- try
- {
- unsafe
- {
- szText = Marshal.StringToCoTaskMemUni(text);
- copyDataStruct.dwData = UIntPtr.Zero;
- copyDataStruct.lpData = szText;
- copyDataStruct.cbData = (uint) (2*(1 + text.Length));
- var lParam = new IntPtr(©DataStruct);
-
- SafeNativeMethods.SendMessageW(ourHwnd, NativeConstants.WM_COPYDATA, _hWnd, lParam);
- }
- }
-
- finally
- {
- if (szText != IntPtr.Zero)
- {
- Marshal.FreeCoTaskMem(szText);
- szText = IntPtr.Zero;
- }
- }
- }
- }
-
- public void FocusFirstInstance()
- {
- IntPtr ourHwnd = ReadHandleFromFromMappedFile();
-
- if (ourHwnd == IntPtr.Zero) return;
- if (SafeNativeMethods.IsIconic(ourHwnd))
- {
- SafeNativeMethods.ShowWindow(ourHwnd, NativeConstants.SW_RESTORE);
- }
-
- SafeNativeMethods.SetForegroundWindow(ourHwnd);
- }
-
- public void FilterMessage(ref Message m)
- {
- if (m.Msg == NativeConstants.WM_COPYDATA)
- {
- unsafe
- {
- var pCopyDataStruct = (NativeStructs.COPYDATASTRUCT*)m.LParam.ToPointer();
- string message = Marshal.PtrToStringUni(pCopyDataStruct->lpData);
-
- lock (_pendingInstanceMessages)
- {
- _pendingInstanceMessages.Add(message);
- }
-
- OnInstanceMessageReceived();
- }
- }
- }
-
- public SingleInstanceManager(string moniker)
- {
- int error = NativeConstants.ERROR_SUCCESS;
-
- if (moniker.IndexOf('\\') != -1)
- {
- throw new ArgumentException("moniker must not have a backslash character");
- }
-
- _mappingName = "Local\\" + moniker;
-
- _hFileMapping = SafeNativeMethods.CreateFileMappingW(
- NativeConstants.INVALID_HANDLE_VALUE,
- IntPtr.Zero,
- NativeConstants.PAGE_READWRITE | NativeConstants.SEC_COMMIT,
- 0,
- MappingSize,
- _mappingName);
-
- error = Marshal.GetLastWin32Error();
-
- if (_hFileMapping == IntPtr.Zero)
- {
- throw new Win32Exception(error, "CreateFileMappingW() returned NULL (" + error + ")");
- }
-
- _isFirstInstance = (error != NativeConstants.ERROR_ALREADY_EXISTS);
- }
-
- private void WriteHandleValueToMappedFile(IntPtr hValue)
- {
- int error = NativeConstants.ERROR_SUCCESS;
- bool bResult = true;
-
- IntPtr lpData = SafeNativeMethods.MapViewOfFile(
- _hFileMapping,
- NativeConstants.FILE_MAP_WRITE,
- 0,
- 0,
- //new UIntPtr((uint)mappingSize));
- new UIntPtr(8));//same as above but computed
-
- error = Marshal.GetLastWin32Error();
-
- if (lpData == IntPtr.Zero)
- {
- throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
- }
-
- long int64 = hValue.ToInt64();
- var int64Bytes = new byte[MappingSize];
-
- for (int i = 0; i < MappingSize; ++i)
- {
- int64Bytes[i] = (byte)((int64 >> (i * 8)) & 0xff);
- }
-
- Marshal.Copy(int64Bytes, 0, lpData, MappingSize);
-
- bResult = SafeNativeMethods.UnmapViewOfFile(lpData);
- error = Marshal.GetLastWin32Error();
-
- if (!bResult)
- {
- throw new Win32Exception(error, "UnmapViewOfFile() returned FALSE (" + error + ")");
- }
- }
-
- private IntPtr ReadHandleFromFromMappedFile()
- {
- int error = NativeConstants.ERROR_SUCCESS;
-
- IntPtr lpData = SafeNativeMethods.MapViewOfFile(
- _hFileMapping,
- NativeConstants.FILE_MAP_READ,
- 0,
- 0,
- new UIntPtr(MappingSize));
-
- error = Marshal.GetLastWin32Error();
-
- if (lpData == IntPtr.Zero)
- {
- throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
- }
-
- var int64Bytes = new byte[MappingSize];
- Marshal.Copy(lpData, int64Bytes, 0, MappingSize);
-
- long int64 = 0;
- for (int i = 0; i < MappingSize; ++i)
- {
- int64 += int64Bytes[i] << (i * 8);
- }
-
- bool bResult = SafeNativeMethods.UnmapViewOfFile(lpData);
- error = Marshal.GetLastWin32Error();
-
- if (!bResult)
- {
- throw new Win32Exception(error, "UnmapViewOfFile() returned FALSE (" + error + ")");
- }
-
- var hValue = new IntPtr(int64);
- return hValue;
- }
-
- ~SingleInstanceManager()
- {
- Dispose(false);
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (disposing)
- {
- UnregisterWindow();
- }
-
- if (_hFileMapping == IntPtr.Zero) return;
- SafeNativeMethods.CloseHandle(_hFileMapping);
- _hFileMapping = IntPtr.Zero;
- }
- }
- }