using System; using System.ComponentModel; using System.Threading; namespace MicaSetup.Shell.Dialogs; public class ShellObjectWatcher : IDisposable { private readonly ShellObject _shellObject; private readonly bool _recursive; private readonly ChangeNotifyEventManager _manager = new(); private readonly nint _listenerHandle; private readonly uint _message; private uint _registrationId; private volatile bool _running; private readonly SynchronizationContext _context = SynchronizationContext.Current; public ShellObjectWatcher(ShellObject shellObject, bool recursive) { if (_context == null) { _context = new SynchronizationContext(); SynchronizationContext.SetSynchronizationContext(_context); } _shellObject = shellObject ?? throw new ArgumentNullException("shellObject"); _recursive = recursive; var result = MessageListenerFilter.Register(OnWindowMessageReceived); _listenerHandle = result.WindowHandle; _message = result.Message; } public bool Running { get => _running; private set => _running = value; } public void Start() { if (Running) { return; } var entry = new SHChangeNotifyEntry { recursively = _recursive, pIdl = _shellObject.PIDL }; _registrationId = Shell32.SHChangeNotifyRegister( _listenerHandle, ShellChangeNotifyEventSource.ShellLevel | ShellChangeNotifyEventSource.InterruptLevel | ShellChangeNotifyEventSource.NewDelivery, _manager.RegisteredTypes, _message, 1, ref entry); if (_registrationId == 0) { throw new Win32Exception(LocalizedMessages.ShellObjectWatcherRegisterFailed); } Running = true; } public void Stop() { if (!Running) { return; } if (_registrationId > 0) { Shell32.SHChangeNotifyDeregister(_registrationId); _registrationId = 0; } Running = false; } private void OnWindowMessageReceived(WindowMessageEventArgs e) { if (e.Message.Msg == _message) { _context.Send(x => ProcessChangeNotificationEvent(e), null); } } private void ThrowIfRunning() { if (Running) { throw new InvalidOperationException(LocalizedMessages.ShellObjectWatcherUnableToChangeEvents); } } protected virtual void ProcessChangeNotificationEvent(WindowMessageEventArgs e) { if (!Running) { return; } if (e == null) { throw new ArgumentNullException("e"); } var notifyLock = new ChangeNotifyLock(e.Message); ShellObjectNotificationEventArgs args = notifyLock.ChangeType switch { ShellObjectChangeTypes.DirectoryRename or ShellObjectChangeTypes.ItemRename => new ShellObjectRenamedEventArgs(notifyLock), ShellObjectChangeTypes.SystemImageUpdate => new SystemImageUpdatedEventArgs(notifyLock), _ => new ShellObjectChangedEventArgs(notifyLock), }; _manager.Invoke(this, notifyLock.ChangeType, args); } public event EventHandler AllEvents { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.AllEventsMask, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.AllEventsMask, value); } } public event EventHandler GlobalEvents { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.GlobalEventsMask, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.GlobalEventsMask, value); } } public event EventHandler DiskEvents { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DiskEventsMask, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DiskEventsMask, value); } } public event EventHandler ItemRenamed { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.ItemRename, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.ItemRename, value); } } public event EventHandler ItemCreated { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.ItemCreate, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.ItemCreate, value); } } public event EventHandler ItemDeleted { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.ItemDelete, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.ItemDelete, value); } } public event EventHandler Updated { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.Update, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.Update, value); } } public event EventHandler DirectoryUpdated { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DirectoryContentsUpdate, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DirectoryContentsUpdate, value); } } public event EventHandler DirectoryRenamed { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DirectoryRename, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DirectoryRename, value); } } public event EventHandler DirectoryCreated { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DirectoryCreate, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DirectoryCreate, value); } } public event EventHandler DirectoryDeleted { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DirectoryDelete, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DirectoryDelete, value); } } public event EventHandler MediaInserted { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.MediaInsert, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.MediaInsert, value); } } public event EventHandler MediaRemoved { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.MediaRemove, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.MediaRemove, value); } } public event EventHandler DriveAdded { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DriveAdd, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DriveAdd, value); } } public event EventHandler DriveRemoved { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.DriveRemove, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.DriveRemove, value); } } public event EventHandler FolderNetworkShared { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.NetShare, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.NetShare, value); } } public event EventHandler FolderNetworkUnshared { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.NetUnshare, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.NetUnshare, value); } } public event EventHandler ServerDisconnected { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.ServerDisconnect, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.ServerDisconnect, value); } } public event EventHandler SystemImageChanged { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.SystemImageUpdate, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.SystemImageUpdate, value); } } public event EventHandler FreeSpaceChanged { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.FreeSpace, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.FreeSpace, value); } } public event EventHandler FileTypeAssociationChanged { add { ThrowIfRunning(); _manager.Register(ShellObjectChangeTypes.AssociationChange, value); } remove { ThrowIfRunning(); _manager.Unregister(ShellObjectChangeTypes.AssociationChange, value); } } protected virtual void Dispose(bool disposing) { Stop(); _manager.UnregisterAll(); if (_listenerHandle != 0) { MessageListenerFilter.Unregister(_listenerHandle, _message); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~ShellObjectWatcher() { Dispose(false); } }