From efdd8da0d1c3288dd40291dd6c12ac7c33fc41c9 Mon Sep 17 00:00:00 2001 From: dahall Date: Wed, 27 May 2020 22:11:36 -0600 Subject: [PATCH] To ShellItemChangeWatcher, added attributes to properties, added Path property tied to Item property for use in designer, error handling if registration fails, and fixed bug with PIDLs and memory reads (#124). --- Windows.Shell/ShellItemChangeWatcher.cs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Windows.Shell/ShellItemChangeWatcher.cs b/Windows.Shell/ShellItemChangeWatcher.cs index acf871bc..f871ee33 100644 --- a/Windows.Shell/ShellItemChangeWatcher.cs +++ b/Windows.Shell/ShellItemChangeWatcher.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Drawing.Design; using System.Linq; +using System.Runtime.InteropServices; using System.Windows.Forms; +using System.Windows.Forms.Design; using Vanara.PInvoke; using static Vanara.PInvoke.Shell32; @@ -168,6 +172,7 @@ namespace Vanara.Windows.Shell } /// Occurs when a shell folder or item is changed. + [Category("Behavior"), Description("Occurs when a shell folder or item is changed.")] public event EventHandler Changed; /// Gets or sets a value indicating whether the component is enabled. @@ -205,7 +210,7 @@ namespace Vanara.Windows.Shell /// Gets or sets the shell item to watch. /// The shell item to monitor. The default is . /// Item - [DefaultValue(null), Category("Data"), Description("The shell item to watch.")] + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(null), Category("Data"), Description("The shell item to watch.")] public ShellItem Item { get => item; @@ -232,6 +237,15 @@ namespace Vanara.Windows.Shell } } + /// Gets or sets the path of the shell item to watch. + /// The path of the shell item to monitor. The default is . + [DefaultValue(null), Category("Data"), Description("The shell item to watch."), Editor(typeof(FileNameEditor), typeof(UITypeEditor))] + public string Path + { + get => item is null ? null : (item.IsFileSystem ? item.FileSystemPath : item.GetDisplayName(ShellItemDisplayString.DesktopAbsoluteParsing)); + set => Item = value is null ? null : new ShellItem(value); + } + private bool IsSuspended => initializing || DesignMode; /// @@ -298,6 +312,7 @@ namespace Vanara.Windows.Shell SHGetIDListFromObject(Item.IShellItem, out var pidlWatch).ThrowIfFailed(); SHChangeNotifyEntry[] entries = { new SHChangeNotifyEntry { pidl = pidlWatch.DangerousGetHandle(), fRecursive = IncludeChildren } }; ulRegister = SHChangeNotifyRegister(hPump.Handle, sources, (SHCNE)NotifyFilter, hPump.MessageId, entries.Length, entries); + if (ulRegister == 0) throw new InvalidOperationException("Unable to register shell notifications."); } private void StopWatching() @@ -317,7 +332,18 @@ namespace Vanara.Windows.Shell internal ShellItemChangeEventArgs(SHCNE levent, IntPtr rgpidl) { ChangeType = (ChangeFilters)levent; - ChangedItems = new PIDL(rgpidl, true).Select(p => new ShellItem(p)).ToArray(); + ChangedItems = GetPidls().Select(p => new ShellItem(new PIDL(p, false, false))).ToArray(); + + IEnumerable GetPidls() + { + var offset = 0; + var ptr = IntPtr.Zero; + while ((ptr = Marshal.ReadIntPtr(rgpidl, offset)) != IntPtr.Zero) + { + yield return ptr; + offset += IntPtr.Size; + } + } } /// Gets the items affected by the change. @@ -351,7 +377,7 @@ namespace Vanara.Windows.Shell try { hNotifyLock = SHChangeNotification_Lock(m.WParam, (uint)m.LParam.ToInt32(), out var rgpidl, out var lEvent); - if (hNotifyLock != IntPtr.Zero) // && (lEvent & (int)(SHCNE.SHCNE_UPDATEIMAGE | SHCNE.SHCNE_ASSOCCHANGED | SHCNE.SHCNE_EXTENDED_EVENT | SHCNE.SHCNE_FREESPACE | SHCNE.SHCNE_DRIVEADDGUI | SHCNE.SHCNE_SERVERDISCONNECT)) != 0 + if (hNotifyLock != IntPtr.Zero && rgpidl != IntPtr.Zero) // && (lEvent & (int)(SHCNE.SHCNE_UPDATEIMAGE | SHCNE.SHCNE_ASSOCCHANGED | SHCNE.SHCNE_EXTENDED_EVENT | SHCNE.SHCNE_FREESPACE | SHCNE.SHCNE_DRIVEADDGUI | SHCNE.SHCNE_SERVERDISCONNECT)) != 0 p.OnChanged(new ShellItemChangeEventArgs(lEvent, rgpidl)); } finally