Windows 10でレジストリから拡張子の関連付けキーのハンドルとその名前を取得するコードです。拡張子の関連付け情報を持つキーハンドルはWin32 APIのAssocQueryKeyW
関数で取得できますが、.NET Core 3.1の標準機能ではレジストリキーハンドル(HKEY
)の名前や場所を取得する方法がありません。ここではWin32 APIのNtQueryKey
関数を使用してこれを解決しています。
using System; using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; class Program { static void Main() { // .ps1はFileExtsの設定が優先 var name1 = QueryAssocKeyNamesForFile(".ps1"); // > ASSOCKEY_SHELLEXECCLASS null // > ASSOCKEY_CLASS "\REGISTRY\USER\<ユーザーのSID>_Classes\Applications\pwsh.exe" // > ASSOCKEY_BASECLASS "\REGISTRY\USER\<ユーザーのSID>_Classes\Applications\pwsh.exe" // > ASSOCKEY_APP "\REGISTRY\USER\<ユーザーのSID>_Classes\Applications\pwsh.exe" // .exeはHKEY_CLASSES_ROOT直下 var name2 = QueryAssocKeyNamesForFile(".exe"); // > ASSOCKEY_SHELLEXECCLASS null // > ASSOCKEY_CLASS null // > ASSOCKEY_BASECLASS "\\REGISTRY\\USER\\<ユーザーのSID>_Classes\\exefile" // > ASSOCKEY_APP "\\REGISTRY\\USER\\<ユーザーのSID>_Classes\\exefile" // .exeにxxxコマンドは存在しない(はず) var name3 = QueryAssocKeyNamesForFile(".exe", "xxx"); // > ASSOCKEY_SHELLEXECCLASS null // > ASSOCKEY_CLASS null // > ASSOCKEY_BASECLASS "\\REGISTRY\\USER\\<ユーザーのSID>_Classes\\exefile" // > ASSOCKEY_APP null } static (string, string, string, string) QueryAssocKeyNamesForFile(string assoc, string extra = null) { using var keyHandle1 = QueryAssocKeyForFile(ASSOCKEY_SHELLEXECCLASS, assoc, extra); using var keyHandle2 = QueryAssocKeyForFile(ASSOCKEY_CLASS, assoc, extra); using var keyHandle3 = QueryAssocKeyForFile(ASSOCKEY_BASECLASS, assoc, extra); using var keyHandle4 = QueryAssocKeyForFile(ASSOCKEY_APP, assoc, extra); return ( QueryRegistryKeyName(keyHandle1), QueryRegistryKeyName(keyHandle2), QueryRegistryKeyName(keyHandle3), QueryRegistryKeyName(keyHandle4)); } private static SafeRegistryHandle QueryAssocKeyForFile( int key, string assoc, string extra = null) { var hr = NativeMethods.AssocQueryKeyW( ASSOCF_INIT_FOR_FILE, key, assoc, extra, out var keyHandle); return hr == 0 ? keyHandle : null; } private static string QueryRegistryKeyName(SafeRegistryHandle handle) { if (handle == null) return null; var lstatus = NativeMethods.NtQueryKey(handle, KEY_INFORMATION_CLASS.KeyNameInformation, null, 0, out var bufferSize); if (lstatus != STATUS_BUFFER_TOO_SMALL) return null; var buffer = new byte[bufferSize]; lstatus = NativeMethods.NtQueryKey(handle, KEY_INFORMATION_CLASS.KeyNameInformation, buffer, bufferSize, out bufferSize); if (lstatus != 0) return null; var nameLength = BitConverter.ToUInt32(buffer, 0); return Encoding.Unicode.GetString(buffer, 4, buffer.Length - 4); } private static class NativeMethods { [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] public static extern int AssocQueryKeyW( int flags, int key, [In] string pszAssoc, [In] string pszExtra, out SafeRegistryHandle phkeyOut); [DllImport("ntdll.dll")] public static extern int NtQueryKey( SafeRegistryHandle KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, byte[] KeyInformation, uint Length, out uint ResultLength); } private const int ASSOCF_INIT_FOR_FILE = 0x00002000; private const int ASSOCKEY_SHELLEXECCLASS = 0; private const int ASSOCKEY_APP = 1; private const int ASSOCKEY_CLASS = 2; private const int ASSOCKEY_BASECLASS = 3; private const int STATUS_BUFFER_TOO_SMALL = unchecked((int)0xC0000023); public enum KEY_INFORMATION_CLASS : int { KeyNameInformation = 3, } }