PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/Python/Product/CanopyInterpreter/RegistryWatcher.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 392 lines | 247 code | 42 blank | 103 comment | 45 complexity | c7d4be144051831083c3f5fef364ac59 MD5 | raw file
  1. // Python Tools for Visual Studio
  2. // Copyright(c) Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. // this file except in compliance with the License. You may obtain a copy of the
  7. // License at http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. // MERCHANTABLITY OR NON-INFRINGEMENT.
  13. //
  14. // See the Apache Version 2.0 License for specific language governing
  15. // permissions and limitations under the License.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.ComponentModel;
  19. using System.Diagnostics;
  20. using System.Linq;
  21. using System.Security.AccessControl;
  22. using System.Threading;
  23. using Microsoft.Win32;
  24. namespace CanopyInterpreter {
  25. /// <summary>
  26. /// Provides information about a detected change to the registry.
  27. /// </summary>
  28. class RegistryChangedEventArgs : EventArgs {
  29. /// <summary>
  30. /// Creates a new event object.
  31. /// </summary>
  32. /// <param name="key">The key that was originally provided.</param>
  33. public RegistryChangedEventArgs(
  34. RegistryHive hive,
  35. RegistryView view,
  36. string key,
  37. bool isRecursive,
  38. bool isValueChange,
  39. bool isKeyChange,
  40. object tag
  41. ) {
  42. Hive = hive;
  43. View = view;
  44. Key = key;
  45. IsRecursive = isRecursive;
  46. IsValueChanged = isValueChange;
  47. IsKeyChanged = isKeyChange;
  48. Tag = tag;
  49. }
  50. /// <summary>
  51. /// The registry hive originally provided to the watcher.
  52. /// </summary>
  53. public RegistryHive Hive { get; private set; }
  54. /// <summary>
  55. /// The registry view originally provided to the watcher.
  56. /// </summary>
  57. public RegistryView View { get; private set; }
  58. /// <summary>
  59. /// The key that was originally provided to the watcher. This may not
  60. /// have changed to trigger the event.
  61. /// </summary>
  62. public string Key { get; private set; }
  63. /// <summary>
  64. /// True if the key and all its subkeys and values were being watched.
  65. /// </summary>
  66. public bool IsRecursive { get; private set; }
  67. /// <summary>
  68. /// True if the key was being watched for value changes. This may be
  69. /// true even if it was not a value change that triggered the event.
  70. /// </summary>
  71. public bool IsValueChanged { get; private set; }
  72. /// <summary>
  73. /// True if the key was being watched for key changes. This may be
  74. /// true even if it was not a key change that triggered the event.
  75. /// </summary>
  76. public bool IsKeyChanged { get; private set; }
  77. /// <summary>
  78. /// The tag that was originally provided to the watcher.
  79. /// </summary>
  80. public object Tag { get; private set; }
  81. /// <summary>
  82. /// Set to True to prevent the watcher from being run again. This is
  83. /// False by default.
  84. /// </summary>
  85. public bool CancelWatcher { get; set; }
  86. }
  87. /// <summary>
  88. /// Represents the method that will handle a registry change event.
  89. /// </summary>
  90. delegate void RegistryChangedEventHandler(object sender, RegistryChangedEventArgs e);
  91. /// <summary>
  92. /// Provides notifications when registry values are modified.
  93. /// </summary>
  94. class RegistryWatcher : IDisposable {
  95. readonly object _eventsLock = new object();
  96. readonly List<WatchEntry> _entries;
  97. List<RegistryWatcher> _extraWatchers;
  98. readonly AutoResetEvent _itemAdded;
  99. bool _shutdown;
  100. readonly Thread _thread;
  101. static Lazy<RegistryWatcher> _instance = new Lazy<RegistryWatcher>(() => new RegistryWatcher());
  102. public static RegistryWatcher Instance { get { return _instance.Value; } }
  103. /// <summary>
  104. /// Creates a new registry watcher. Each watcher will consume one CPU
  105. /// thread for every 64 objects.
  106. /// </summary>
  107. public RegistryWatcher() {
  108. _entries = new List<WatchEntry>();
  109. _entries.Add(new WatchEntry());
  110. _itemAdded = _entries[0].EventHandle;
  111. _thread = new Thread(Worker);
  112. _thread.IsBackground = true;
  113. _thread.Start(this);
  114. }
  115. public void Dispose() {
  116. if (_shutdown == false) {
  117. _shutdown = true;
  118. _itemAdded.Set();
  119. var extras = _extraWatchers;
  120. _extraWatchers = null;
  121. if (extras != null) {
  122. foreach (var watcher in extras) {
  123. watcher.Dispose();
  124. }
  125. }
  126. }
  127. }
  128. /// <summary>
  129. /// Starts listening for notifications in the specified registry key.
  130. ///
  131. /// Each part of the key must be provided separately so that the watcher
  132. /// can open its own handle.
  133. /// </summary>
  134. /// <param name="hive">The hive to watch</param>
  135. /// <param name="view">The view to watch</param>
  136. /// <param name="key">The key to watch</param>
  137. /// <param name="handler">The event handler to invoke</param>
  138. /// <param name="recursive">True to watch all subkeys as well</param>
  139. /// <param name="notifyValueChange">
  140. /// True to notify if a value is added, removed or updated.
  141. /// </param>
  142. /// <param name="notifyKeyChange">
  143. /// True to notify if a subkey is added or removed.
  144. /// </param>
  145. /// <param name="tag">
  146. /// An arbitrary identifier to include with any raised events.
  147. /// </param>
  148. /// <returns>An opaque token that can be pased to Remove.</returns>
  149. public object Add(RegistryHive hive,
  150. RegistryView view,
  151. string key,
  152. RegistryChangedEventHandler handler,
  153. bool recursive = false,
  154. bool notifyValueChange = true,
  155. bool notifyKeyChange = true,
  156. object tag = null) {
  157. if (key == null) {
  158. throw new ArgumentNullException("key");
  159. }
  160. if (handler == null) {
  161. throw new ArgumentNullException("handler");
  162. }
  163. if (!(notifyValueChange | notifyKeyChange)) {
  164. throw new InvalidOperationException("Must wait for at least one type of change");
  165. }
  166. var args = new RegistryChangedEventArgs(
  167. hive,
  168. view,
  169. key,
  170. recursive,
  171. notifyValueChange,
  172. notifyKeyChange,
  173. tag
  174. );
  175. int currentWatcher = -1;
  176. RegistryWatcher watcher;
  177. var token = AddInternal(handler, args);
  178. while (token == null) {
  179. if (_extraWatchers == null) {
  180. _extraWatchers = new List<RegistryWatcher>();
  181. }
  182. currentWatcher += 1;
  183. if (currentWatcher >= _extraWatchers.Count) {
  184. watcher = new RegistryWatcher();
  185. _extraWatchers.Add(watcher);
  186. } else {
  187. watcher = _extraWatchers[currentWatcher];
  188. }
  189. token = watcher.AddInternal(handler, args);
  190. }
  191. return token;
  192. }
  193. private object AddInternal(RegistryChangedEventHandler handler, RegistryChangedEventArgs args) {
  194. WatchEntry newEntry;
  195. lock (_eventsLock) {
  196. if (_entries.Count >= NativeMethods.MAXIMUM_WAIT_OBJECTS) {
  197. return null;
  198. }
  199. newEntry = new WatchEntry(handler, args);
  200. _entries.Add(newEntry);
  201. }
  202. _itemAdded.Set();
  203. return newEntry;
  204. }
  205. /// <summary>
  206. ///
  207. /// </summary>
  208. /// <param name="key"></param>
  209. /// <returns></returns>
  210. public bool Remove(object token) {
  211. if (token == null) {
  212. throw new ArgumentNullException("token");
  213. }
  214. var entry = token as WatchEntry;
  215. if (entry == null) {
  216. return false;
  217. }
  218. lock (_eventsLock) {
  219. if (_entries.Remove(entry)) {
  220. entry.Unregister();
  221. return true;
  222. }
  223. }
  224. var extras = _extraWatchers;
  225. if (extras != null) {
  226. foreach (var watcher in extras) {
  227. if (watcher.Remove(token)) {
  228. return true;
  229. }
  230. }
  231. }
  232. return false;
  233. }
  234. private static void Worker(object param) {
  235. var self = (RegistryWatcher)param;
  236. WaitHandle[] handles;
  237. WatchEntry[] entries;
  238. int triggeredHandle;
  239. while (!self._shutdown) {
  240. lock (self._eventsLock) {
  241. entries = self._entries.ToArray();
  242. handles = entries.Select(e => e.EventHandle).ToArray();
  243. }
  244. triggeredHandle = WaitHandle.WaitAny(handles);
  245. if (triggeredHandle >= 0 && triggeredHandle < entries.Length && entries[triggeredHandle] != null) {
  246. if (!entries[triggeredHandle].Invoke(self)) {
  247. self.Remove(entries[triggeredHandle]);
  248. }
  249. }
  250. }
  251. }
  252. private sealed class WatchEntry : IDisposable {
  253. /// <summary>
  254. /// Creates a WatchEntry that has an event but does not watch a
  255. /// registry key. All functions become no-ops, but
  256. /// <see cref="EventHandle"/> is valid.
  257. /// </summary>
  258. public WatchEntry() {
  259. _eventHandle = new AutoResetEvent(false);
  260. }
  261. public WatchEntry(RegistryChangedEventHandler callback, RegistryChangedEventArgs args) {
  262. using (var baseKey = RegistryKey.OpenBaseKey(args.Hive, args.View)) {
  263. _key = baseKey.OpenSubKey(args.Key, RegistryKeyPermissionCheck.Default, RegistryRights.Notify);
  264. }
  265. if (_key == null) {
  266. throw new ArgumentException("Key does not exist");
  267. }
  268. _eventHandle = new AutoResetEvent(false);
  269. _callback = callback;
  270. _args = args;
  271. Register();
  272. }
  273. private readonly AutoResetEvent _eventHandle;
  274. private readonly RegistryKey _key;
  275. private readonly RegistryChangedEventHandler _callback;
  276. private readonly RegistryChangedEventArgs _args;
  277. private bool _registered;
  278. private bool _disposed;
  279. public void Dispose() {
  280. if (!_disposed) {
  281. _disposed = true;
  282. _eventHandle.Dispose();
  283. if (_key != null) {
  284. _key.Dispose();
  285. }
  286. }
  287. }
  288. public AutoResetEvent EventHandle { get { return _eventHandle; } }
  289. public void Register() {
  290. if (_key == null) {
  291. return;
  292. }
  293. NativeMethods.RegNotifyChangeKeyValue(
  294. _key,
  295. _eventHandle,
  296. _args.IsRecursive,
  297. (_args.IsValueChanged ? NativeMethods.RegNotifyChange.Value : 0) |
  298. (_args.IsKeyChanged ? NativeMethods.RegNotifyChange.Name : 0));
  299. _registered = true;
  300. }
  301. public void Unregister() {
  302. if (_key == null) {
  303. return;
  304. }
  305. if (_registered) {
  306. lock (this) {
  307. _registered = false;
  308. _key.Close();
  309. }
  310. }
  311. }
  312. /// <summary>
  313. /// Invokes the associated event handler.
  314. /// </summary>
  315. /// <returns>True if the watcher will be run again; false if the
  316. /// entry has been closed and can be removed.</returns>
  317. public bool Invoke(RegistryWatcher sender) {
  318. if (_key == null) {
  319. // Returns true so we don't try and remove null entries from
  320. // the list.
  321. return true;
  322. }
  323. if (!_registered) {
  324. return false;
  325. }
  326. _callback(sender, _args);
  327. if (_args.CancelWatcher) {
  328. Unregister();
  329. return false;
  330. }
  331. lock (this) {
  332. // only re-register if we haven't been closed
  333. if (_registered) {
  334. try {
  335. Register();
  336. } catch (Win32Exception ex) {
  337. // If we fail to re-register (probably because the
  338. // key has been deleted), there's nothing that can
  339. // be done. Fail if we're debugging, otherwise just
  340. // continue without registering the watcher again.
  341. Debug.Fail("Error registering registry watcher: " + ex.ToString());
  342. _registered = false;
  343. }
  344. }
  345. }
  346. return true;
  347. }
  348. }
  349. }
  350. }