Windowsではシェルに「shell:~」形式の名前を与えると特殊フォルダを開けます。C#でこの形式からパスを取得するには、一度Win32 APIのParseDisplayName
関数でアイテムIDリストを取得して、取得したアイテムIDリストをWin32 APIのSHGetPathFromIDListEx
関数等に与えます。SHGetPathFromIDListEx
関数は文字列バッファーの長さを受け取りますが、長さが元のパスの長さに満たなくても成功を返すことに注意が必要です。
クラス | 概要 |
---|---|
Program | Main関数です。 |
ShellIDListUtility | シェルのアイテムIDリストのユーティリティ関数を提供するクラスです。Win32 APIのラッパーです。 |
SafeCoTaskMemPointer | アンマネージドリソースのMarshal.FreeCoTaskMem関数による解放を保証するためのクラスです。SafeHandleの派生クラスです。 |
using System; using System.Runtime.InteropServices; using System.Text; class Program { static void Main() { using var pidl = ShellIDListUtility.ParseDisplayName("shell:common startup"); var path = ShellIDListUtility.GetPath(pidl); } } internal static class ShellIDListUtility { public static SafeCoTaskMemPointer ParseDisplayName(string name) { NativeMethods.SHParseDisplayName(name, default, out var pidl, 0, out var _); return pidl; } public static string GetPath(SafeHandle pidl) { const int EXPANDING = 260; var builder = new StringBuilder(); for (int i = EXPANDING; i < int.MaxValue; i += EXPANDING) { builder.Capacity = i; NativeMethods.SHGetPathFromIDListEx( pidl, builder, (uint)builder.Capacity, 0); if (builder.Length != i - 1) { return builder.ToString(); } } return null; } private static class NativeMethods { [DllImport("shell32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] public static extern int SHParseDisplayName( [In] string pszName, IntPtr 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] StringBuilder pszPath, uint cchPath, uint uOpts); } } internal sealed class SafeCoTaskMemPointer : SafeHandle { private SafeCoTaskMemPointer() : base(default, true) { } public SafeCoTaskMemPointer(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) { } public override bool IsInvalid => handle == default; protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } }