diff --git a/PInvoke/Shell32/ShObjIdl.IShellFolder.cs b/PInvoke/Shell32/ShObjIdl.IShellFolder.cs index e9c4cb4f..91d1742b 100644 --- a/PInvoke/Shell32/ShObjIdl.IShellFolder.cs +++ b/PInvoke/Shell32/ShObjIdl.IShellFolder.cs @@ -351,16 +351,57 @@ namespace Vanara.PInvoke object GetUIObjectOf(HWND hwndOwner, uint cidl, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] IntPtr[] apidl, in Guid riid, IntPtr rgfReserved = default); /// Retrieves the display name for the specified file object or subfolder. - /// PIDL that uniquely identifies the file object or subfolder relative to the parent folder. + /// + /// Type: PCUITEMID_CHILD + /// PIDL that uniquely identifies the file object or subfolder relative to the parent folder. + /// /// + /// Type: SHGDNF + /// /// Flags used to request the type of display name to return. For a list of possible values, see the SHGDNF enumerated type. + /// + /// + /// + /// + /// When this method returns, contains the display name. The type of name returned in this structure can be the requested type, + /// but the Shell folder might return a different type. + /// /// /// - /// When this method returns, contains a pointer to a STRRET structure in which to return the display name. The type of name - /// returned in this structure can be the requested type, but the Shell folder might return a different type. + /// Type: HRESULT + /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// - [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(STRRETMarshaler))] - string GetDisplayNameOf([In] PIDL pidl, SHGDNF uFlags); + /// + /// + /// Normally, pidl can refer only to items contained by the parent folder. The PIDL must be single-level and contain exactly one + /// SHITEMID structure followed by a terminating zero. If you want to retrieve the display name of an item that is deeper than + /// one level away from the parent folder, use SHBindToParent to bind with the item's immediate parent folder and then pass the + /// item's single-level PIDL to IShellFolder::GetDisplayNameOf. + /// + /// + /// Also, if the SHGDN_FORPARSING flag is set in uFlags and the SHGDN_INFOLDER flag is not set, pidl can refer to an object at + /// any level below the parent folder in the namespace hierarchy. At one time, pidl could be a multilevel PIDL, relative to the + /// parent folder, and could contain multiple SHITEMID structures. However, this is no longer supported and pidl should now + /// refer only to a single child item. + /// + /// + /// The flags specified in uFlags are hints about the intended use of the name. They do not guarantee that IShellFolder will + /// return the requested form of the name. If that form is not available, a different one might be returned. In particular, + /// there is no guarantee that the name returned by the SHGDN_FORPARSING flag will be successfully parsed by + /// IShellFolder::ParseDisplayName. There are also some combinations of flags that might cause the GetDisplayNameOf/ + /// ParseDisplayName round trip to not return the original identifier list. This occurrence is exceptional, but you + /// should check to be sure. + /// + /// + /// Note The parsing name that is returned when uFlags has the SHGDN_FORPARSING flag set is not necessarily a normal text + /// string. Virtual folders such as My Computer might return a string containing the folder object's GUID in the form + /// "::{GUID}". Developers who implement IShellFolder::GetDisplayNameOf are encouraged to return parse names that are as + /// close to the display names as possible, because the end user often needs to type or edit these names. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellfolder-getdisplaynameof + // HRESULT GetDisplayNameOf( PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName ); + void GetDisplayNameOf([In] PIDL pidl, SHGDNF uFlags, out STRRET pName); //[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(STRRETMarshaler))] out string pName); /// Sets the display name of a file object or subfolder, changing the item identifier in the process. /// A handle to the owner window of any dialog or message box that the client displays. diff --git a/PInvoke/Shell32/ShTypes.cs b/PInvoke/Shell32/ShTypes.cs index 0ba1b2a5..21834e65 100644 --- a/PInvoke/Shell32/ShTypes.cs +++ b/PInvoke/Shell32/ShTypes.cs @@ -152,9 +152,15 @@ namespace Vanara.PInvoke [FieldOffset(4)] public StrPtrAnsi cStr; - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => (uType == STRRET_TYPE.STRRET_CSTR ? cStr : (uType == STRRET_TYPE.STRRET_WSTR ? pOleStr : (string)null)) ?? string.Empty; + /// Performs an implicit conversion from to . + /// The instance. + /// The result of the conversion. + public static implicit operator string(in STRRET s) => + ShlwApi.StrRetToBSTR(new PinnedObject(s), default, out var ret).Succeeded ? ret : null; + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => (string)this ?? ""; } internal class STRRETMarshaler : ICustomMarshaler @@ -182,15 +188,8 @@ namespace Vanara.PInvoke return sr.MarshalToPtr(Marshal.AllocCoTaskMem, out var _); } - public object MarshalNativeToManaged(IntPtr pNativeData) - { - if (pNativeData == IntPtr.Zero) return null; - var sr = pNativeData.ToStructure(); - var s = sr.ToString().Clone() as string; - if (sr.uType == STRRET_TYPE.STRRET_WSTR) - sr.pOleStr.Free(); - return s; - } + public object MarshalNativeToManaged(IntPtr pNativeData) => + pNativeData != IntPtr.Zero && ShlwApi.StrRetToBSTR(pNativeData, default, out var ret).Succeeded ? ret : null; } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Shell32/Methods.cs b/UnitTests/PInvoke/Shell32/Methods.cs index 05ccbbbc..62451056 100644 --- a/UnitTests/PInvoke/Shell32/Methods.cs +++ b/UnitTests/PInvoke/Shell32/Methods.cs @@ -1,5 +1,7 @@ using NUnit.Framework; +using System; using System.Linq; +using System.Runtime.InteropServices; using static Vanara.PInvoke.Shell32; namespace Vanara.PInvoke.Tests @@ -35,6 +37,22 @@ namespace Vanara.PInvoke.Tests //Assert.That(AssocGetDetailsOfPropKey(), Is.Zero); } + [Test] + public void SHGetDesktopFolderTest() + { + Assert.That(SHGetDesktopFolder(out var sf), ResultIs.Successful); + var eo = sf.EnumObjects(HWND.NULL, SHCONTF.SHCONTF_NONFOLDERS); + Assert.That(eo, Is.Not.Null); + foreach (var sub in new Collections.IEnumFromNext((out IntPtr p) => eo.Next(1, out p, out var f).Succeeded && f == 1, () => { try { eo.Reset(); } catch { } })) + { + STRRET name = default; + Assert.That(() => sf.GetDisplayNameOf(sub, SHGDNF.SHGDN_NORMAL | SHGDNF.SHGDN_INFOLDER, out name), Throws.Nothing); + TestContext.WriteLine(name); + } + Marshal.ReleaseComObject(eo); + Marshal.ReleaseComObject(sf); + } + /* AssocCreateForClasses AssocGetDetailsOfPropKey @@ -136,7 +154,6 @@ namespace Vanara.PInvoke.Tests SHFreeNameMappings SHGetAttributesFromDataObject SHGetDataFromIDList - SHGetDesktopFolder SHGetDiskFreeSpaceA SHGetDiskFreeSpaceEx SHGetDiskFreeSpaceW