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