追記20240902 現行のC# 12ではNull許容型、IntPtr
の代わりにはnint
が使えます。SHGetKnownFolderPath
関数のP/Invokeでは[MarshalAs(UnmanagedType.LPWStr)] out string ppszPath
で文字列取得とメモリ解放が可能です。
C# 9.0
using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; var FOLDERID_DocumentsLibrary = new Guid("{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}"); Console.WriteLine(SHGetKnownFolderPath(FOLDERID_DocumentsLibrary, 0)); [return: MaybeNull] static string SHGetKnownFolderPath( in Guid knownFolderID, uint flags = 0, IntPtr tokenHandle = default) { SafeCoTaskMemHandle handle = default; NativeMethods.SHGetKnownFolderPath(knownFolderID, flags, tokenHandle, out handle); using (handle) { return Marshal.PtrToStringUni(handle.DangerousGetHandle()); } } static class NativeMethods { [DllImport("shell32.dll")] public static extern int SHGetKnownFolderPath( in Guid rfid, uint dwFlags, IntPtr hToken, out SafeCoTaskMemHandle ppszPath); } sealed class SafeCoTaskMemHandle : SafeHandle { private SafeCoTaskMemHandle() : base(IntPtr.Zero, true) { } public SafeCoTaskMemHandle(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) { } public override bool IsInvalid => handle == IntPtr.Zero; protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(this.handle); return true; } }
C# 4.5
失敗時は例外が発生することに注意してください。
using System; using System.Runtime.InteropServices; using System.Security; namespace ConsoleApplication1 { [SecurityCritical] class Program { [SuppressUnmanagedCodeSecurity] private static class NativeMethods { [DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)] public static extern SafeCoTaskMemHandle SHGetKnownFolderPath( ref Guid rfid, uint dwFlags, IntPtr hToken); } static void Main() { Guid FOLDERID_DocumentsLibrary = new Guid( "{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}"); Console.WriteLine(SHGetKnownFolderPath( FOLDERID_DocumentsLibrary, 0, IntPtr.Zero)); Console.WriteLine("PRESS ANY KEY."); Console.ReadKey(); } public static string SHGetKnownFolderPath( Guid knownFolderID, uint flags, IntPtr tokenHandle) { using (var handle = NativeMethods.SHGetKnownFolderPath( ref knownFolderID, flags, tokenHandle)) { return Marshal.PtrToStringUni( handle.DangerousGetHandle()); } } } public sealed class SafeCoTaskMemHandle : SafeHandle { private SafeCoTaskMemHandle() : base(IntPtr.Zero, true) { // 戻り値としての作成に対応します。 } public SafeCoTaskMemHandle(IntPtr handle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) { SetHandle(handle); } public override bool IsInvalid { get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(this.handle); return true; } } }
2021/3/10:この記事は別のブログで投稿した記事を移動したものです。