From a46337952b6ba289cf6489ff65827c67f41c642b Mon Sep 17 00:00:00 2001 From: dahall Date: Thu, 3 Dec 2020 19:37:38 -0700 Subject: [PATCH] Added `ShellItem.ContextMenu` and `Images` properties, enhanced `GetHandler` method to check for correct interfaces, changed `InvokeVerb` to use `ShellContextMenu` --- Windows.Shell/ShellObjects/ShellItem.cs | 114 +++++++++----------------------- 1 file changed, 30 insertions(+), 84 deletions(-) diff --git a/Windows.Shell/ShellObjects/ShellItem.cs b/Windows.Shell/ShellObjects/ShellItem.cs index 0a6f8a57..5e54f359 100644 --- a/Windows.Shell/ShellObjects/ShellItem.cs +++ b/Windows.Shell/ShellObjects/ShellItem.cs @@ -344,6 +344,8 @@ namespace Vanara.Windows.Shell internal IShellItem iShellItem; internal IShellItem2 iShellItem2; private static Dictionary bhidLookup; + private ShellItemImages images; + private ShellContextMenu menu; private PropertyDescriptionList propDescList; private ShellItemPropertyStore props; private IQueryInfo qi; @@ -370,7 +372,7 @@ namespace Vanara.Windows.Shell /// Initializes a new instance of the class. /// An existing IShellItem instance. - protected ShellItem(IShellItem si) => Init(si); + protected internal ShellItem(IShellItem si) => Init(si); /// Initializes a new instance of the class. /// A known folder reference. @@ -405,6 +407,10 @@ namespace Vanara.Windows.Shell /// The attributes of the Shell item. public ShellItemAttribute Attributes => (ShellItemAttribute)(iShellItem?.GetAttributes((SFGAO)0xFFFFFFFF) ?? 0); + /// Gets the context menu detail for this shell item. + /// The context menu. + public ShellContextMenu ContextMenu => menu ??= new ShellContextMenu(this); + /// Returns a representing the item. This object is used in drag and drop operations. public System.Windows.Forms.DataObject DataObject => new System.Windows.Forms.DataObject(GetHandler(BHID.BHID_SFUIObject)); @@ -415,6 +421,9 @@ namespace Vanara.Windows.Shell /// The file system path. public string FileSystemPath => GetDisplayName(SIGDN.SIGDN_FILESYSPATH); + /// Gets an object that provides access to all the images available for this shell item. + public ShellItemImages Images => images ??= new ShellItemImages(this); + /// Gets a value indicating whether this instance is part of the file system. /// true if this instance is part of the file system; otherwise, false. public bool IsFileSystem => (iShellItem?.GetAttributes(SFGAO.SFGAO_FILESYSTEM) ?? 0) != 0; @@ -603,10 +612,19 @@ namespace Vanara.Windows.Shell public TInterface GetHandler(IBindCtx bindCtx, BHID handler = 0) where TInterface : class { if (handler == 0) - handler = GetBHIDForInterface(); - if (handler == 0) - throw new ArgumentOutOfRangeException(nameof(handler)); - return iShellItem.BindToHandler(bindCtx, handler.Guid(), typeof(TInterface).GUID) as TInterface; + { + if ((handler = GetBHIDForInterface()) == 0) + throw new ArgumentOutOfRangeException(nameof(handler)); + } + else if (!ConfirmHandlerValid()) + throw new InvalidOperationException("BHID value cannot retrieve requested type."); + return iShellItem.BindToHandler(bindCtx, handler.Guid()); + + bool ConfirmHandlerValid() + { + var types = CorrespondingTypeAttribute.GetCorrespondingTypes(handler, CorrespondingAction.Get).ToArray(); + return types.Length == 0 || types.Contains(typeof(TInterface)); + } } /// Returns a hash code for this instance. @@ -621,22 +639,7 @@ namespace Vanara.Windows.Shell /// One or more of the option flags. /// The resulting image. /// - public Image GetImage(Size size, ShellItemGetImageOptions flags) - { - if (!IsMinVista) throw new PlatformNotSupportedException(); - if (iShellItem is IShellItemImageFactory fctry) - { - var hres = fctry.GetImage(size, (SIIGBF)flags, out var hbitmap); - if (hres == 0x8004B200 && flags.IsFlagSet(ShellItemGetImageOptions.ThumbnailOnly)) - throw new InvalidOperationException("Thumbnails are not supported by this item."); - hres.ThrowIfFailed(); - //Marshal.ReleaseComObject(fctry); - return GetTransparentBitmap(hbitmap); - } - if (!flags.IsFlagSet(ShellItemGetImageOptions.IconOnly)) - return GetThumbnail(size.Width); - throw new InvalidOperationException("Unable to retrieve an image for this item."); - } + public Image GetImage(Size size, ShellItemGetImageOptions flags) => Images.GetImageAsync(size, flags).Result.ToBitmap(); /// Gets a property description list object given a reference to a property key. /// @@ -670,13 +673,13 @@ namespace Vanara.Windows.Shell /// The tool tip text formatted as per . public string GetToolTip(ShellItemToolTipOptions options = ShellItemToolTipOptions.Default) { - if (qi == null) + if (qi is null) try { qi = (Parent ?? ShellFolder.Desktop).GetChildrenUIObjects(null, this); } catch { } - if (qi == null) return ""; + if (qi is null) return ""; qi.GetInfoTip((QITIP)options, out var ret); return ret ?? ""; } @@ -690,24 +693,8 @@ namespace Vanara.Windows.Shell /// public void InvokeVerb(string verb, string args = null, bool hideUI = false) { - OleInitialize(default); // Not sure why necessary, but it fails without - - // Get parent and relative child PIDL - ((IParentAndItem)iShellItem).GetParentAndItem(out _, out var iFolder, out var idChild).ThrowIfFailed(); - using var pParent = new ComReleaser(iFolder); - - // Get IContext menu for item and load via query - using var pMenu = ComReleaserFactory.Create(pParent.Item.GetUIObjectOf(default, new[] { idChild })); - using var hMenu = CreateMenu(); - pMenu.Item.QueryContextMenu(hMenu, 0, 0, 0x7FF, CMF.CMF_EXPLORE).ThrowIfFailed(); - - // Setup structure and invoke command - using var pVerb = new SafeResourceId(verb, CharSet.Ansi); - var ici = new CMINVOKECOMMANDINFOEX { cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)), - lpVerb = pVerb, nShow = ShowWindowCommand.SW_NORMAL, lpParameters = args }; - if (hideUI) - ici.fMask |= CMIC.CMIC_MASK_FLAG_NO_UI; - pMenu.Item.InvokeCommand(ici); + using var pVerb = new SafeResourceId(verb); + ContextMenu.InvokeCommand(pVerb, parent: hideUI ? HWND.NULL : GetDesktopWindow(), parameters: args); } /// Returns a that represents this instance. @@ -788,6 +775,8 @@ namespace Vanara.Windows.Shell { typeof(IShellItemResources), BHID.BHID_SFViewObject }, { typeof(IShellFolder2), BHID.BHID_SFViewObject }, + { typeof(IShellView), BHID.BHID_SFViewObject }, + { typeof(IDropTarget), BHID.BHID_SFViewObject }, { typeof(IStorage), BHID.BHID_Storage }, @@ -888,49 +877,6 @@ namespace Vanara.Windows.Shell return left.Compare(right, SICHINTF.SICHINT_CANONICAL | SICHINTF.SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL) == 0; } - private static Bitmap GetTransparentBitmap(HBITMAP hbitmap) - { - try - { - var dibsection = GetObject(hbitmap); - var bitmap = new Bitmap(dibsection.dsBm.bmWidth, dibsection.dsBm.bmHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - using var mstr = new NativeMemoryStream(dibsection.dsBm.bmBits, dibsection.dsBmih.biSizeImage); - for (var x = 0; x < dibsection.dsBmih.biWidth; x++) - for (var y = 0; y < dibsection.dsBmih.biHeight; y++) - { - var rgbquad = mstr.Read(); - if (rgbquad.rgbReserved != 0) - bitmap.SetPixel(x, y, rgbquad.Color); - } - return bitmap; - } - catch { } - return Image.FromHbitmap((IntPtr)hbitmap); - } - - /// Gets the thumbnail image for the item using the specified characteristics. - /// The width, in pixels, of the Bitmap. - /// The resulting Bitmap, on success, or null on failure. - private Image GetThumbnail(int width = 32) - { - IThumbnailProvider provider = null; - try - { - provider = GetHandler(BHID.BHID_ThumbnailHandler); - if (provider == null) return null; - provider.GetThumbnail((uint)width, out var hbmp, out var alpha); - return hbmp.ToBitmap(); - } - catch - { - return null; - } - finally - { - if (provider != null) Marshal.ReleaseComObject(provider); - } - } - /// Local implementation of IShellItem. /// ///