PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/SystemLayer/SingleInstaceManager.cs

https://bitbucket.org/tuldok89/openpdn
C# | 349 lines | 302 code | 35 blank | 12 comment | 25 complexity | 831d4dbcf679aa47a7cb54ca2c77b24b MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System;
  10. using System.Collections.Generic;
  11. using System.ComponentModel;
  12. using System.Runtime.InteropServices;
  13. using System.Windows.Forms;
  14. namespace PaintDotNet.SystemLayer
  15. {
  16. /// <summary>
  17. /// Provides a way to manage and communicate between instances of an application
  18. /// in the same user session.
  19. /// </summary>
  20. public sealed class SingleInstanceManager
  21. : IDisposable
  22. {
  23. private const int MappingSize = 8; // sizeof(int64)
  24. private readonly string _mappingName;
  25. private Form _window;
  26. private IntPtr _hWnd = IntPtr.Zero;
  27. private IntPtr _hFileMapping;
  28. private readonly List<string> _pendingInstanceMessages = new List<string>();
  29. private readonly bool _isFirstInstance;
  30. public bool IsFirstInstance
  31. {
  32. get
  33. {
  34. return _isFirstInstance;
  35. }
  36. }
  37. public bool AreMessagesPending
  38. {
  39. get
  40. {
  41. lock (_pendingInstanceMessages)
  42. {
  43. return (_pendingInstanceMessages.Count > 0);
  44. }
  45. }
  46. }
  47. public void SetWindow(Form newWindow)
  48. {
  49. if (_window != null)
  50. {
  51. UnregisterWindow();
  52. }
  53. RegisterWindow(newWindow);
  54. }
  55. private void UnregisterWindow()
  56. {
  57. if (_window == null) return;
  58. _window.HandleCreated -= WindowHandleCreated;
  59. _window.HandleDestroyed -= WindowHandleDestroyed;
  60. _window.Disposed -= WindowDisposed;
  61. WriteHandleValueToMappedFile(IntPtr.Zero);
  62. _hWnd = IntPtr.Zero;
  63. _window = null;
  64. }
  65. private void RegisterWindow(Form newWindow)
  66. {
  67. _window = newWindow;
  68. if (_window != null)
  69. {
  70. _window.HandleCreated += WindowHandleCreated;
  71. _window.HandleDestroyed += WindowHandleDestroyed;
  72. _window.Disposed += WindowDisposed;
  73. if (_window.IsHandleCreated)
  74. {
  75. _hWnd = _window.Handle;
  76. WriteHandleValueToMappedFile(_hWnd);
  77. }
  78. }
  79. GC.KeepAlive(newWindow);
  80. }
  81. private void WindowDisposed(object sender, EventArgs e)
  82. {
  83. UnregisterWindow();
  84. }
  85. private void WindowHandleDestroyed(object sender, EventArgs e)
  86. {
  87. UnregisterWindow();
  88. }
  89. private void WindowHandleCreated(object sender, EventArgs e)
  90. {
  91. _hWnd = _window.Handle;
  92. WriteHandleValueToMappedFile(_hWnd);
  93. GC.KeepAlive(_window);
  94. }
  95. public string[] GetPendingInstanceMessages()
  96. {
  97. string[] messages;
  98. lock (_pendingInstanceMessages)
  99. {
  100. messages = _pendingInstanceMessages.ToArray();
  101. _pendingInstanceMessages.Clear();
  102. }
  103. return messages;
  104. }
  105. public event EventHandler InstanceMessageReceived;
  106. private void OnInstanceMessageReceived()
  107. {
  108. if (InstanceMessageReceived != null)
  109. {
  110. InstanceMessageReceived(this, EventArgs.Empty);
  111. }
  112. }
  113. public void SendInstanceMessage(string text)
  114. {
  115. SendInstanceMessage(text, 1);
  116. }
  117. public void SendInstanceMessage(string text, int timeoutSeconds)
  118. {
  119. IntPtr ourHwnd = IntPtr.Zero;
  120. DateTime now = DateTime.Now;
  121. DateTime timeoutTime = DateTime.Now + new TimeSpan(0, 0, 0, timeoutSeconds);
  122. while (ourHwnd == IntPtr.Zero && now < timeoutTime)
  123. {
  124. ourHwnd = ReadHandleFromFromMappedFile();
  125. now = DateTime.Now;
  126. if (ourHwnd == IntPtr.Zero)
  127. {
  128. System.Threading.Thread.Sleep(100);
  129. }
  130. }
  131. if (ourHwnd == IntPtr.Zero)
  132. {
  133. }
  134. else
  135. {
  136. var copyDataStruct = new NativeStructs.COPYDATASTRUCT();
  137. IntPtr szText = IntPtr.Zero;
  138. try
  139. {
  140. unsafe
  141. {
  142. szText = Marshal.StringToCoTaskMemUni(text);
  143. copyDataStruct.dwData = UIntPtr.Zero;
  144. copyDataStruct.lpData = szText;
  145. copyDataStruct.cbData = (uint) (2*(1 + text.Length));
  146. var lParam = new IntPtr(&copyDataStruct);
  147. SafeNativeMethods.SendMessageW(ourHwnd, NativeConstants.WM_COPYDATA, _hWnd, lParam);
  148. }
  149. }
  150. finally
  151. {
  152. if (szText != IntPtr.Zero)
  153. {
  154. Marshal.FreeCoTaskMem(szText);
  155. szText = IntPtr.Zero;
  156. }
  157. }
  158. }
  159. }
  160. public void FocusFirstInstance()
  161. {
  162. IntPtr ourHwnd = ReadHandleFromFromMappedFile();
  163. if (ourHwnd == IntPtr.Zero) return;
  164. if (SafeNativeMethods.IsIconic(ourHwnd))
  165. {
  166. SafeNativeMethods.ShowWindow(ourHwnd, NativeConstants.SW_RESTORE);
  167. }
  168. SafeNativeMethods.SetForegroundWindow(ourHwnd);
  169. }
  170. public void FilterMessage(ref Message m)
  171. {
  172. if (m.Msg == NativeConstants.WM_COPYDATA)
  173. {
  174. unsafe
  175. {
  176. var pCopyDataStruct = (NativeStructs.COPYDATASTRUCT*)m.LParam.ToPointer();
  177. string message = Marshal.PtrToStringUni(pCopyDataStruct->lpData);
  178. lock (_pendingInstanceMessages)
  179. {
  180. _pendingInstanceMessages.Add(message);
  181. }
  182. OnInstanceMessageReceived();
  183. }
  184. }
  185. }
  186. public SingleInstanceManager(string moniker)
  187. {
  188. int error = NativeConstants.ERROR_SUCCESS;
  189. if (moniker.IndexOf('\\') != -1)
  190. {
  191. throw new ArgumentException("moniker must not have a backslash character");
  192. }
  193. _mappingName = "Local\\" + moniker;
  194. _hFileMapping = SafeNativeMethods.CreateFileMappingW(
  195. NativeConstants.INVALID_HANDLE_VALUE,
  196. IntPtr.Zero,
  197. NativeConstants.PAGE_READWRITE | NativeConstants.SEC_COMMIT,
  198. 0,
  199. MappingSize,
  200. _mappingName);
  201. error = Marshal.GetLastWin32Error();
  202. if (_hFileMapping == IntPtr.Zero)
  203. {
  204. throw new Win32Exception(error, "CreateFileMappingW() returned NULL (" + error + ")");
  205. }
  206. _isFirstInstance = (error != NativeConstants.ERROR_ALREADY_EXISTS);
  207. }
  208. private void WriteHandleValueToMappedFile(IntPtr hValue)
  209. {
  210. int error = NativeConstants.ERROR_SUCCESS;
  211. bool bResult = true;
  212. IntPtr lpData = SafeNativeMethods.MapViewOfFile(
  213. _hFileMapping,
  214. NativeConstants.FILE_MAP_WRITE,
  215. 0,
  216. 0,
  217. //new UIntPtr((uint)mappingSize));
  218. new UIntPtr(8));//same as above but computed
  219. error = Marshal.GetLastWin32Error();
  220. if (lpData == IntPtr.Zero)
  221. {
  222. throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
  223. }
  224. long int64 = hValue.ToInt64();
  225. var int64Bytes = new byte[MappingSize];
  226. for (int i = 0; i < MappingSize; ++i)
  227. {
  228. int64Bytes[i] = (byte)((int64 >> (i * 8)) & 0xff);
  229. }
  230. Marshal.Copy(int64Bytes, 0, lpData, MappingSize);
  231. bResult = SafeNativeMethods.UnmapViewOfFile(lpData);
  232. error = Marshal.GetLastWin32Error();
  233. if (!bResult)
  234. {
  235. throw new Win32Exception(error, "UnmapViewOfFile() returned FALSE (" + error + ")");
  236. }
  237. }
  238. private IntPtr ReadHandleFromFromMappedFile()
  239. {
  240. int error = NativeConstants.ERROR_SUCCESS;
  241. IntPtr lpData = SafeNativeMethods.MapViewOfFile(
  242. _hFileMapping,
  243. NativeConstants.FILE_MAP_READ,
  244. 0,
  245. 0,
  246. new UIntPtr(MappingSize));
  247. error = Marshal.GetLastWin32Error();
  248. if (lpData == IntPtr.Zero)
  249. {
  250. throw new Win32Exception(error, "MapViewOfFile() returned NULL (" + error + ")");
  251. }
  252. var int64Bytes = new byte[MappingSize];
  253. Marshal.Copy(lpData, int64Bytes, 0, MappingSize);
  254. long int64 = 0;
  255. for (int i = 0; i < MappingSize; ++i)
  256. {
  257. int64 += int64Bytes[i] << (i * 8);
  258. }
  259. bool bResult = SafeNativeMethods.UnmapViewOfFile(lpData);
  260. error = Marshal.GetLastWin32Error();
  261. if (!bResult)
  262. {
  263. throw new Win32Exception(error, "UnmapViewOfFile() returned FALSE (" + error + ")");
  264. }
  265. var hValue = new IntPtr(int64);
  266. return hValue;
  267. }
  268. ~SingleInstanceManager()
  269. {
  270. Dispose(false);
  271. }
  272. public void Dispose()
  273. {
  274. Dispose(true);
  275. GC.SuppressFinalize(this);
  276. }
  277. private void Dispose(bool disposing)
  278. {
  279. if (disposing)
  280. {
  281. UnregisterWindow();
  282. }
  283. if (_hFileMapping == IntPtr.Zero) return;
  284. SafeNativeMethods.CloseHandle(_hFileMapping);
  285. _hFileMapping = IntPtr.Zero;
  286. }
  287. }
  288. }