123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- 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 string mappingName;
- private Form window = null;
- private IntPtr hWnd = IntPtr.Zero;
- private IntPtr hFileMapping;
- private List<string> pendingInstanceMessages = new List<string>();
- private bool isFirstInstance;
- public bool IsFirstInstance
- {
- get
- {
- return this.isFirstInstance;
- }
- }
- public bool AreMessagesPending
- {
- get
- {
- lock (this.pendingInstanceMessages)
- {
- return (this.pendingInstanceMessages.Count > 0);
- }
- }
- }
- public void SetWindow(Form newWindow)
- {
- if (this.window != null)
- {
- UnregisterWindow();
- }
- RegisterWindow(newWindow);
- }
- private void UnregisterWindow()
- {
- if (this.window != null)
- {
- this.window.HandleCreated -= new EventHandler(Window_HandleCreated);
- this.window.HandleDestroyed -= new EventHandler(Window_HandleDestroyed);
- this.window.Disposed -= new EventHandler(Window_Disposed);
- WriteHandleValueToMappedFile(IntPtr.Zero);
- this.hWnd = IntPtr.Zero;
- this.window = null;
- }
- }
- private void RegisterWindow(Form newWindow)
- {
- this.window = newWindow;
- if (this.window != null)
- {
- this.window.HandleCreated += new EventHandler(Window_HandleCreated);
- this.window.HandleDestroyed += new EventHandler(Window_HandleDestroyed);
- this.window.Disposed += new EventHandler(Window_Disposed);
- if (this.window.IsHandleCreated)
- {
- this.hWnd = this.window.Handle;
- WriteHandleValueToMappedFile(this.hWnd);
- }
- }
- GC.KeepAlive(newWindow);
- }
- private void Window_Disposed(object sender, EventArgs e)
- {
- UnregisterWindow();
- }
- private void Window_HandleDestroyed(object sender, EventArgs e)
- {
- UnregisterWindow();
- }
- private void Window_HandleCreated(object sender, EventArgs e)
- {
- this.hWnd = this.window.Handle;
- WriteHandleValueToMappedFile(this.hWnd);
- GC.KeepAlive(this.window);
- }
- public string[] GetPendingInstanceMessages()
- {
- string[] messages;
- lock (this.pendingInstanceMessages)
- {
- messages = this.pendingInstanceMessages.ToArray();
- this.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)
- {
- NativeStructs.COPYDATASTRUCT 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));
- IntPtr lParam = new IntPtr((void*)©DataStruct);
- SafeNativeMethods.SendMessageW(ourHwnd, NativeConstants.WM_COPYDATA, this.hWnd, lParam);
- }
- }
- finally
- {
- if (szText != IntPtr.Zero)
- {
- Marshal.FreeCoTaskMem(szText);
- szText = IntPtr.Zero;
- }
- }
- }
- }
- public void FocusFirstInstance()
- {
- IntPtr ourHwnd = this.ReadHandleFromFromMappedFile();
- if (ourHwnd != IntPtr.Zero)
- {
- 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
- {
- NativeStructs.COPYDATASTRUCT* pCopyDataStruct = (NativeStructs.COPYDATASTRUCT*)m.LParam.ToPointer();
- string message = Marshal.PtrToStringUni(pCopyDataStruct->lpData);
- lock (this.pendingInstanceMessages)
- {
- this.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");
- }
- this.mappingName = "Local\\" + moniker;
- this.hFileMapping = SafeNativeMethods.CreateFileMappingW(
- NativeConstants.INVALID_HANDLE_VALUE,
- IntPtr.Zero,
- NativeConstants.PAGE_READWRITE | NativeConstants.SEC_COMMIT,
- 0,
- mappingSize,
- mappingName);
- error = Marshal.GetLastWin32Error();
- if (this.hFileMapping == IntPtr.Zero)
- {
- throw new Win32Exception(error, "CreateFileMappingW() returned NULL (" + error.ToString() + ")");
- }
- this.isFirstInstance = (error != NativeConstants.ERROR_ALREADY_EXISTS);
- }
- private void WriteHandleValueToMappedFile(IntPtr hValue)
- {
- int error = NativeConstants.ERROR_SUCCESS;
- bool bResult = true;
- IntPtr lpData = SafeNativeMethods.MapViewOfFile(
- this.hFileMapping,
- NativeConstants.FILE_MAP_WRITE,
- 0,
- 0,
- new UIntPtr((uint)mappingSize));
- error = Marshal.GetLastWin32Error();
- if (lpData == IntPtr.Zero)
- {
- throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
- }
- long int64 = hValue.ToInt64();
- byte[] int64Bytes = new byte[(int)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(
- this.hFileMapping,
- NativeConstants.FILE_MAP_READ,
- 0,
- 0,
- new UIntPtr((uint)mappingSize));
- error = Marshal.GetLastWin32Error();
- if (lpData == IntPtr.Zero)
- {
- throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
- }
- byte[] int64Bytes = new byte[(int)mappingSize];
- Marshal.Copy(lpData, int64Bytes, 0, mappingSize);
- long int64 = 0;
- for (int i = 0; i < mappingSize; ++i)
- {
- int64 += (long)(int64Bytes[i] << (i * 8));
- }
- bool bResult = SafeNativeMethods.UnmapViewOfFile(lpData);
- error = Marshal.GetLastWin32Error();
- if (!bResult)
- {
- throw new Win32Exception(error, "UnmapViewOfFile() returned FALSE (" + error + ")");
- }
- IntPtr 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 (this.hFileMapping != IntPtr.Zero)
- {
- SafeNativeMethods.CloseHandle(this.hFileMapping);
- this.hFileMapping = IntPtr.Zero;
- }
- }
- }
- }
|