/// <summary> /// Retrieves the attributes of one or more file objects or subfolders. /// Return value: error code, if any /// </summary> /// <param name="cidl">Number of file objects from which to retrieve attributes.</param> /// <param name="apidl">Address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object relative to the parent folder.</param> /// <param name="rgfInOut">Address of a single ULONG value that, on entry contains the attributes that the caller is /// requesting. On exit, this value contains the requested attributes that are common to all of the specified objects. this value can be from the SFGAO enum</param> /// <returns> /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// </returns> /// <exception cref="System.NotImplementedException"></exception> int IShellFolder.GetAttributesOf(uint cidl, IntPtr apidl, ref SFGAO rgfInOut) { // Get each id list. var idlists = PidlManager.APidlToIdListArray(apidl, (int)cidl); // Now we can ask for the attributes of each item. We only ask for attributes that // are set in the flags - clearing them if they don't apply to every item. var allItems = idlists.Select(GetChildItem).ToList(); var allAttributes = allItems.Select(sni => sni.GetAttributes()).ToList(); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_BROWSABLE, allAttributes.All(a => a.HasFlag(AttributeFlags.IsBrowsable))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_CANCOPY, allAttributes.All(a => a.HasFlag(AttributeFlags.CanByCopied))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_CANDELETE, allAttributes.All(a => a.HasFlag(AttributeFlags.CanBeDeleted))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_CANLINK, allAttributes.All(a => a.HasFlag(AttributeFlags.CanBeLinked))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_CANMOVE, allAttributes.All(a => a.HasFlag(AttributeFlags.CanBeMoved))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_CANRENAME, allAttributes.All(a => a.HasFlag(AttributeFlags.CanBeRenamed))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_COMPRESSED, allAttributes.All(a => a.HasFlag(AttributeFlags.IsCompressed))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_DROPTARGET, allAttributes.All(a => a.HasFlag(AttributeFlags.IsDropTarget))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_ENCRYPTED, allAttributes.All(a => a.HasFlag(AttributeFlags.IsEncrypted))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_FILESYSANCESTOR, allAttributes.All(a => a.HasFlag(AttributeFlags.IsFileSystemAncestor))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_FILESYSTEM, allAttributes.All(a => a.HasFlag(AttributeFlags.IsFileSystem))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_FOLDER, allAttributes.All(a => a.HasFlag(AttributeFlags.IsFolder))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_GHOSTED, allAttributes.All(a => a.HasFlag(AttributeFlags.IsBrowsable))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_HASPROPSHEET, allAttributes.All(a => a.HasFlag(AttributeFlags.HasPropertySheets))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_HASSUBFOLDER, allAttributes.All(a => a.HasFlag(AttributeFlags.MayContainSubFolders))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_HIDDEN, allAttributes.All(a => a.HasFlag(AttributeFlags.IsHidden))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_ISSLOW, allAttributes.All(a => a.HasFlag(AttributeFlags.IsSlow))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_LINK, allAttributes.All(a => a.HasFlag(AttributeFlags.IsLink))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_NEWCONTENT, allAttributes.All(a => a.HasFlag(AttributeFlags.HasOrIsNewContent))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_READONLY, allAttributes.All(a => a.HasFlag(AttributeFlags.IsReadOnly))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_REMOVABLE, allAttributes.All(a => a.HasFlag(AttributeFlags.IsRemovableMedia))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_SHARE, allAttributes.All(a => a.HasFlag(AttributeFlags.IsShared))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_STORAGE, allAttributes.All(a => a.HasFlag(AttributeFlags.IsStorage))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_STORAGEANCESTOR, allAttributes.All(a => a.HasFlag(AttributeFlags.IsStorageAncestor))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_STREAM, allAttributes.All(a => a.HasFlag(AttributeFlags.IsStream))); UpdateFlagIfSet(ref rgfInOut, SFGAO.SFGAO_VALIDATE, allAttributes.All(a => a.HasFlag(AttributeFlags.IsVolatile))); // And we're done. return(WinError.S_OK); }
/// <summary> /// Retrieves an OLE interface that can be used to carry out actions on the /// specified file objects or folders. Return value: error code, if any /// </summary> /// <param name="hwndOwner">Handle to the owner window that the client should specify if it displays a dialog box or message box.</param> /// <param name="cidl">Number of file objects or subfolders specified in the apidl parameter.</param> /// <param name="apidl">Address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object or subfolder relative to the parent folder.</param> /// <param name="riid">Identifier of the COM interface object to return.</param> /// <param name="rgfReserved">Reserved.</param> /// <param name="ppv">Pointer to the requested interface.</param> /// <returns> /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// </returns> int IShellFolder.GetUIObjectOf(IntPtr hwndOwner, uint cidl, IntPtr apidl, ref Guid riid, uint rgfReserved, out IntPtr ppv) { // Get the ID lists from the array of PIDLs provided. var idLists = PidlManager.APidlToIdListArray(apidl, (int)cidl); if (riid == typeof(IContextMenu).GUID || riid == typeof(IContextMenu2).GUID || riid == typeof(IContextMenu3).GUID) { // If the folder implments the context menu provider, we can use that. var contextMenuProvider = proxyFolder as IShellNamespaceFolderContextMenuProvider; if (contextMenuProvider != null) { ppv = Marshal.GetComInterfaceForObject(contextMenuProvider.CreateContextMenu(idListAbsolute, idLists), typeof(IContextMenu)); return(WinError.S_OK); } var dcm = new DEFCONTEXTMENU { hwnd = hwndOwner, pcmcb = null, pidlFolder = PidlManager.IdListToPidl(idListAbsolute), psf = this, cidl = cidl, apidl = apidl, punkAssociationInfo = IntPtr.Zero, cKeys = 0, aKeys = null }; // Create the default context menu. var result = Shell32.SHCreateDefaultContextMenu(dcm, riid, out ppv); } else if (riid == Shell32.IID_ExtractIconW) { // If we've been asked for an icon, it should only be for a single PIDL. if (idLists.Length != 1) { Diagnostics.Logging.Error(string.Format("The Shell Folder Impl for folder {0} has been asked for icons for multiple files at once, this is not supportedd.", proxyFolder.GetDisplayName(DisplayNameContext.Normal))); ppv = IntPtr.Zero; return(WinError.E_FAIL); } // Get the idlist and item. var idList = idLists[0]; var item = GetChildItem(idList); // Now get the icon. If we don't provide one we'll use the defaults. var icon = item.GetIcon(); if (icon == null) { ProvideDefaultIExtractIcon(item is IShellNamespaceFolder, out ppv); return(WinError.S_OK); } else { // Create an icon provider. var provider = new Components.ExtractIconImpl() { DoNotCacheIcons = false, Icon = icon }; ppv = Marshal.GetComInterfaceForObject(provider, typeof(IExtractIconW)); return(WinError.S_OK); } } else if (riid == Shell32.IID_IDataObject) { // Create the data object. Shell32.SHCreateDataObject(PidlManager.IdListToPidl(idListAbsolute), cidl, apidl, null, riid, out ppv); } else if (riid == Shell32.IID_IQueryAssociations) { // If we've been asked for a query associations, it should only be for a single PIDL. if (idLists.Length != 1) { Diagnostics.Logging.Error(string.Format("The Shell Folder Impl for folder {0} has been asked for query associations for multiple files at once, this is not supportedd.", proxyFolder.GetDisplayName(DisplayNameContext.Normal))); ppv = IntPtr.Zero; return(WinError.E_FAIL); } var item = GetChildItem(idLists[0]); var isFolder = item is IShellNamespaceFolder; if (isFolder) { // todo perhaps a good class name would simply be the // name of the item type? or an attribute that uses the classname as a // fallback. var associations = new ASSOCIATIONELEMENT[] { new ASSOCIATIONELEMENT { ac = ASSOCCLASS.ASSOCCLASS_PROGID_STR, hkClass = IntPtr.Zero, pszClass = "FolderViewSampleType" }, new ASSOCIATIONELEMENT { ac = ASSOCCLASS.ASSOCCLASS_FOLDER, hkClass = IntPtr.Zero, pszClass = "FolderViewSampleType" } }; Shell32.AssocCreateForClasses(associations, (uint)associations.Length, riid, out ppv); } else { var associations = new ASSOCIATIONELEMENT[] { new ASSOCIATIONELEMENT { ac = ASSOCCLASS.ASSOCCLASS_PROGID_STR, hkClass = IntPtr.Zero, pszClass = "FolderViewSampleType" } }; Shell32.AssocCreateForClasses(associations, (uint)associations.Length, riid, out ppv); } } /* } */ // We have a set of child pidls (i.e. length one). We can now offer interfaces such as: /* * IContextMenu The cidl parameter can be greater than or equal to one. * IContextMenu2 The cidl parameter can be greater than or equal to one. * IDataObject The cidl parameter can be greater than or equal to one. * IDropTarget The cidl parameter can only be one. * IExtractIcon The cidl parameter can only be one. * IQueryInfo The cidl parameter can only be one. * */ // IID_IExtractIconW // IID_IDataObject // IID_IQueryAssociations // Currently, we don't offer any extra child item UI objects. ppv = IntPtr.Zero; return(WinError.E_NOINTERFACE); }