過去の投稿では関数を小分けにしていましたが、一つの機能としてまとめる関数を作成しても良いと思います。ついでに投稿後に追加されたトップレベルステートメントも使用しています。
using System.Runtime.InteropServices; Console.WriteLine(ShellIDListUtility.GetPathFromDisplayName("shell:common startup")); internal static class ShellIDListUtility { public static SafeCoTaskMemPointer ParseDisplayName(string name) { var hr = NativeMethods.SHParseDisplayName(name, 0, out var pidl, 0, out var _); if (hr < 0) Marshal.ThrowExceptionForHR(hr); return pidl; } public static string GetPath(SafeHandle pidl) { const int EXPANDING = 260; for (int i = EXPANDING; i < int.MaxValue; i += EXPANDING) { var buffer = GC.AllocateUninitializedArray<char>(i); if (!NativeMethods.SHGetPathFromIDListEx(pidl, buffer, (uint)i, 0)) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); var len = Array.IndexOf(buffer, '\0'); if (len != i - 1) return new string(buffer, 0, len); } throw new InvalidDataException(); } public static string GetPathFromDisplayName(string name) { using var pidl = ParseDisplayName(name); return GetPath(pidl); } private static class NativeMethods { [DllImport("shell32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] public static extern int SHParseDisplayName( string pszName, nint pbc, out SafeCoTaskMemPointer ppidl, uint sfgaoIn, out uint psfgaoOut); [DllImport("shell32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SHGetPathFromIDListEx( SafeHandle pidl, [Out] char[] pszPath, uint cchPath, uint uOpts); } } internal sealed class SafeCoTaskMemPointer : SafeHandle { public SafeCoTaskMemPointer() : base(0, true) { } public SafeCoTaskMemPointer(nint handle, bool ownsHandle) : base(handle, ownsHandle) { } public override bool IsInvalid => handle == 0; protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } }