BREAKING CHANGE: Fixed problems with STRRET and its use in IShellFolder::GetDisplayNameOf. #73

pull/83/head
David Hall 2019-11-23 18:24:59 -07:00
parent 42771630ad
commit 37b9f50288
3 changed files with 75 additions and 18 deletions

View File

@ -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);
/// <summary>Retrieves the display name for the specified file object or subfolder.</summary>
/// <param name="pidl">PIDL that uniquely identifies the file object or subfolder relative to the parent folder.</param>
/// <param name="pidl">
/// <para>Type: <c>PCUITEMID_CHILD</c></para>
/// <para>PIDL that uniquely identifies the file object or subfolder relative to the parent folder.</para>
/// </param>
/// <param name="uFlags">
/// <para>Type: <c>SHGDNF</c></para>
/// <para>
/// Flags used to request the type of display name to return. For a list of possible values, see the SHGDNF enumerated type.
/// </para>
/// </param>
/// <param name="pName">
/// <para>
/// 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.
/// </para>
/// </param>
/// <returns>
/// 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.
/// <para>Type: <c>HRESULT</c></para>
/// <para>If this method succeeds, it returns <c>S_OK</c>. Otherwise, it returns an <c>HRESULT</c> error code.</para>
/// </returns>
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(STRRETMarshaler))]
string GetDisplayNameOf([In] PIDL pidl, SHGDNF uFlags);
/// <remarks>
/// <para>
/// 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 <c>IShellFolder::GetDisplayNameOf</c>.
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>
/// 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 <c>GetDisplayNameOf</c>/
/// <c>ParseDisplayName</c> round trip to not return the original identifier list. This occurrence is exceptional, but you
/// should check to be sure.
/// </para>
/// <para>
/// <c>Note</c> 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 <c>IShellFolder::GetDisplayNameOf</c> 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.
/// </para>
/// </remarks>
// 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);
/// <summary>Sets the display name of a file object or subfolder, changing the item identifier in the process.</summary>
/// <param name="hwnd">A handle to the owner window of any dialog or message box that the client displays.</param>

View File

@ -152,9 +152,15 @@ namespace Vanara.PInvoke
[FieldOffset(4)]
public StrPtrAnsi cStr;
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public override string ToString() => (uType == STRRET_TYPE.STRRET_CSTR ? cStr : (uType == STRRET_TYPE.STRRET_WSTR ? pOleStr : (string)null)) ?? string.Empty;
/// <summary>Performs an implicit conversion from <see cref="STRRET"/> to <see cref="System.String"/>.</summary>
/// <param name="s">The <see cref="STRRET"/> instance.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator string(in STRRET s) =>
ShlwApi.StrRetToBSTR(new PinnedObject(s), default, out var ret).Succeeded ? ret : null;
/// <summary>Returns a <see cref="string"/> that represents this instance.</summary>
/// <returns>A <see cref="string"/> that represents this instance.</returns>
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<STRRET>();
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;
}
}
}

View File

@ -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<IntPtr>((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