C# .NET5.0(.net5.0-windows)でWindowsのプロパティシステムの情報を操作する自分用のサンプルコードを記録しています。
シェルアイテムのプロパティの名前から値(PROPVARIANT
)を取得する。
プロパティシステムはCOMインターフェイス(IPropertySystem
やIPropertyStore
等)を利用していますが、propsys.dll
の公開するヘルパー関数を利用すれば簡単な操作はインターフェイスを意識せず実施できます。コードの流れ:
- シェルアイテムの名前からシェルアイテムのプロパティストアを作成する。
- プロパティの既知の名前からプロパティディスクリプタ―を作成する。
- プロパティストアとプロパティディスクリプタ―からプロパティの値を
PROPVARIANT
構造体として取得する。
using System; using System.Runtime.InteropServices; class Program { static void Main() { using var propValue = GetPropertyValueFromParsingName( path: Environment.SystemDirectory, propName: "System.FileOwner", GETPROPERTYSTOREFLAGS.GPS_DEFAULT); // TODO:ここでPROPVARIANTを処理します。 Console.WriteLine("vt:{0}", propValue.vt); // 出力例:"vt:31" } private static PropVariant GetPropertyValueFromParsingName( in string path, in string propName, GETPROPERTYSTOREFLAGS storeFlags) { // try-finallyで確実に解放するアンマネージリソース var propStore = default(object); var propDesc = default(object); var value = default(PropVariant); try { // シェルアイテムの名前からプロパティストアの作成 propStore = NativeMethods.SHGetPropertyStoreFromParsingName( path, null, storeFlags, IID_IUnknown); // プロパティ名に対応するプロパティディスクリプタの取得 propDesc = NativeMethods.PSGetPropertyDescriptionByName( propName, IID_IUnknown); // プロパティストアからプロパティディスクリプタに対応する値の取得 value = PropVariant.GetPropertyValue(propStore, propDesc); // コピーの返却 return value; } catch { value.Clear(); throw; } finally { // アンマネージリソースの解放 Marshal.FinalReleaseComObject(propStore); Marshal.FinalReleaseComObject(propDesc); } } private static Guid IID_IUnknown => new Guid("{00000000-0000-0000-C000-000000000046}"); private static class NativeMethods { [DllImport("shell32.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object SHGetPropertyStoreFromParsingName( [In] string pszPath, [MarshalAs(UnmanagedType.IUnknown)] object pbc, GETPROPERTYSTOREFLAGS flags, in Guid riid); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object PSGetPropertyDescriptionByName( [In] string pszCanonicalName, in Guid riid); } [Flags] public enum GETPROPERTYSTOREFLAGS { GPS_DEFAULT = 0, GPS_HANDLERPROPERTIESONLY = 0x1, GPS_READWRITE = 0x2, GPS_TEMPORARY = 0x4, GPS_FASTPROPERTIESONLY = 0x8, GPS_OPENSLOWITEM = 0x10, GPS_DELAYCREATION = 0x20, GPS_BESTEFFORT = 0x40, GPS_NO_OPLOCK = 0x80, GPS_PREFERQUERYPROPERTIES = 0x100, GPS_EXTRINSICPROPERTIES = 0x200, GPS_EXTRINSICPROPERTIESONLY = 0x400, GPS_VOLATILEPROPERTIES = 0x800, GPS_VOLATILEPROPERTIESONLY = 0x1000, GPS_MASK_VALID = 0x1fff } public sealed class PropVariant : IDisposable { private PROPVARIANT value; private PropVariant(in PROPVARIANT value) { this.value = value; } ~PropVariant() { Dispose(); } public void Clear() { NativeMethods.PropVariantClear(ref value); } public void Dispose() { Clear(); } public ushort vt { get => this.value.vt; set => this.value.vt = value; } public static PropVariant GetPropertyValue( object propertyStore, object propertyDescription) { NativeMethods.PSGetPropertyValue( propertyStore, propertyDescription, out var pv); return new PropVariant(pv); } private static class NativeMethods { [DllImport("ole32.dll", ExactSpelling = true)] public static extern void PropVariantClear(ref PROPVARIANT pvar); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true)] public static extern void PSGetPropertyValue( [MarshalAs(UnmanagedType.IUnknown)] object pps, [MarshalAs(UnmanagedType.IUnknown)] object ppd, out PROPVARIANT ppropvar); } [StructLayout(LayoutKind.Sequential, Size = 24)] public struct PROPVARIANT { public ushort vt; } } }
シェルアイテムのプロパティの名前から値(書式化文字列)を取得する。
コードの流れ:
- シェルアイテムの名前からシェルアイテムのプロパティストアを作成する。
- プロパティの既知の名前からプロパティディスクリプタ―を作成する。
- プロパティストアとプロパティディスクリプタ―からプロパティの値を書式化して取得する。
using System; using System.Runtime.InteropServices; class Program { static void Main() { var propValue = FormatPropertyValueFromParsingName( path: Environment.SystemDirectory, propName: "System.FileOwner", GETPROPERTYSTOREFLAGS.GPS_DEFAULT, PROPDESC_FORMAT_FLAGS.PDFF_NOAUTOREADINGORDER); // TODO:ここで書式化したプロパティ値を処理します。 Console.WriteLine(propValue); // 出力例:TrustedInstaller } private static string FormatPropertyValueFromParsingName( in string path, in string propName, GETPROPERTYSTOREFLAGS storeFlags, PROPDESC_FORMAT_FLAGS formatFlags) { // try-finallyで確実に解放するアンマネージリソース var propStore = default(object); var propDesc = default(object); try { // シェルアイテムの名前からプロパティストアの作成 propStore = NativeMethods.SHGetPropertyStoreFromParsingName( path, null, storeFlags, IID_IUnknown); // プロパティ名に対応するプロパティディスクリプタの取得 propDesc = NativeMethods.PSGetPropertyDescriptionByName( propName, IID_IUnknown); // プロパティストアからプロパティディスクリプタに対応する値の取得 using var p = NativeMethods.PSFormatPropertyValue(propStore, propDesc, formatFlags); return Marshal.PtrToStringUni(p.DangerousGetHandle()); } finally { // アンマネージリソースの解放 Marshal.FinalReleaseComObject(propStore); Marshal.FinalReleaseComObject(propDesc); } } private static Guid IID_IUnknown => new Guid("{00000000-0000-0000-C000-000000000046}"); private static class NativeMethods { [DllImport("shell32.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object SHGetPropertyStoreFromParsingName( [In] string pszPath, [MarshalAs(UnmanagedType.IUnknown)] object pbc, GETPROPERTYSTOREFLAGS flags, in Guid riid); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object PSGetPropertyDescriptionByName( [In] string pszCanonicalName, in Guid riid); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] public static extern SafeCoTaskMemHandle PSFormatPropertyValue( [MarshalAs(UnmanagedType.IUnknown)] object pps, [MarshalAs(UnmanagedType.IUnknown)] object ppd, PROPDESC_FORMAT_FLAGS pdff); } [Flags] public enum GETPROPERTYSTOREFLAGS { GPS_DEFAULT = 0, GPS_HANDLERPROPERTIESONLY = 0x1, GPS_READWRITE = 0x2, GPS_TEMPORARY = 0x4, GPS_FASTPROPERTIESONLY = 0x8, GPS_OPENSLOWITEM = 0x10, GPS_DELAYCREATION = 0x20, GPS_BESTEFFORT = 0x40, GPS_NO_OPLOCK = 0x80, GPS_PREFERQUERYPROPERTIES = 0x100, GPS_EXTRINSICPROPERTIES = 0x200, GPS_EXTRINSICPROPERTIESONLY = 0x400, GPS_VOLATILEPROPERTIES = 0x800, GPS_VOLATILEPROPERTIESONLY = 0x1000, GPS_MASK_VALID = 0x1fff } public enum PROPDESC_FORMAT_FLAGS { PDFF_DEFAULT, PDFF_PREFIXNAME, PDFF_FILENAME, PDFF_ALWAYSKB, PDFF_RESERVED_RIGHTTOLEFT, PDFF_SHORTTIME, PDFF_LONGTIME, PDFF_HIDETIME, PDFF_SHORTDATE, PDFF_LONGDATE, PDFF_HIDEDATE, PDFF_RELATIVEDATE, PDFF_USEEDITINVITATION, PDFF_READONLY, PDFF_NOAUTOREADINGORDER } private sealed class SafeCoTaskMemHandle : SafeHandle { private SafeCoTaskMemHandle() :base(default(IntPtr), true) { } public SafeCoTaskMemHandle(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) { } public override bool IsInvalid => handle == default(IntPtr); protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } } }
PROPVARIANT
をobject
型に変換する。
C#のobject
型は既定のマーシャリングでVARIANT
型として扱われるため、WinAPIのPropVariantToVariant
関数、VariantToPropVariant
関数でobject
型へ変換できます。
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; class Program { static void Main() { using var propValue = GetPropertyValueFromParsingName( path: Environment.SystemDirectory, propName: "System.FileOwner", GETPROPERTYSTOREFLAGS.GPS_DEFAULT); // TODO:ここでPROPVARIANTを処理します。 var obj = propValue.ToObject(); Console.WriteLine("vt:{0}", propValue.vt); // 出力例:"vt:31" } private static PropVariant GetPropertyValueFromParsingName( in string path, in string propName, GETPROPERTYSTOREFLAGS storeFlags) { // try-finallyで確実に解放するアンマネージリソース var propStore = default(object); var propDesc = default(object); var value = default(PropVariant); try { // シェルアイテムの名前からプロパティストアの作成 propStore = NativeMethods.SHGetPropertyStoreFromParsingName( path, null, storeFlags, IID_IUnknown); // プロパティ名に対応するプロパティディスクリプタの取得 propDesc = NativeMethods.PSGetPropertyDescriptionByName( propName, IID_IUnknown); // プロパティストアからプロパティディスクリプタに対応する値の取得 value = PropVariant.GetPropertyValue(propStore, propDesc); // コピーの返却 return value; } catch { value.Clear(); throw; } finally { // アンマネージリソースの解放 Marshal.FinalReleaseComObject(propStore); Marshal.FinalReleaseComObject(propDesc); } } private static Guid IID_IUnknown => new Guid("{00000000-0000-0000-C000-000000000046}"); private static class NativeMethods { [DllImport("shell32.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object SHGetPropertyStoreFromParsingName( [In] string pszPath, [MarshalAs(UnmanagedType.IUnknown)] object pbc, GETPROPERTYSTOREFLAGS flags, in Guid riid); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.IUnknown)] public static extern object PSGetPropertyDescriptionByName( [In] string pszCanonicalName, in Guid riid); } [Flags] public enum GETPROPERTYSTOREFLAGS { GPS_DEFAULT = 0, GPS_HANDLERPROPERTIESONLY = 0x1, GPS_READWRITE = 0x2, GPS_TEMPORARY = 0x4, GPS_FASTPROPERTIESONLY = 0x8, GPS_OPENSLOWITEM = 0x10, GPS_DELAYCREATION = 0x20, GPS_BESTEFFORT = 0x40, GPS_NO_OPLOCK = 0x80, GPS_PREFERQUERYPROPERTIES = 0x100, GPS_EXTRINSICPROPERTIES = 0x200, GPS_EXTRINSICPROPERTIESONLY = 0x400, GPS_VOLATILEPROPERTIES = 0x800, GPS_VOLATILEPROPERTIESONLY = 0x1000, GPS_MASK_VALID = 0x1fff } public sealed class PropVariant : IDisposable { private PROPVARIANT value; private PropVariant(in PROPVARIANT value) { this.value = value; } public void Clear() { NativeMethods.PropVariantClear(ref value); } public void Dispose() { Clear(); } public ushort vt { get => this.value.vt; set => this.value.vt = value; } public static PropVariant GetPropertyValue( object propertyStore, object propertyDescription) { NativeMethods.PSGetPropertyValue( propertyStore, propertyDescription, out var value); return new PropVariant(value); } public static PropVariant FromObject(object value) { NativeMethods.VariantToPropVariant(value, out var pv); return new PropVariant(pv); } public object ToObject() { return NativeMethods.PropVariantToVariant(value); } private static class NativeMethods { [DllImport("ole32.dll", ExactSpelling = true)] public static extern void PropVariantClear(ref PROPVARIANT pvar); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true)] public static extern void PSGetPropertyValue( [MarshalAs(UnmanagedType.IUnknown)] object pps, [MarshalAs(UnmanagedType.IUnknown)] object ppd, out PROPVARIANT ppropvar); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true)] public static extern object PropVariantToVariant(in PROPVARIANT pPropVar); [DllImport("propsys.dll", PreserveSig = false, ExactSpelling = true)] public static extern void VariantToPropVariant([In] object pVar, out PROPVARIANT pPropVar); } [StructLayout(LayoutKind.Sequential, Size = 24)] public struct PROPVARIANT { public ushort vt; } } }