potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# 9&Win API カレントユーザーのレジストリに登録された実行ファイルのAppIDを取得する

カレントユーザーのレジストリに登録された実行ファイルのAppIDを取得するサンプルです。HKEY_CLASSES_ROOT\AppIDレジストリキーのうち、キー名が{GUID}形式ではないキーを扱います。AppIDレジストリキーの詳細はMicrosoft Docsを参照ください。

// 必要なNuGetパッケージ:
// Microsoft.Win32.Registry

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.Win32;

var exeNameNames = AppIDKeyExeNameInfo.GetNames();
foreach (var name in exeNameNames)
{
    using var info = AppIDKeyExeNameInfo.Create(name);
    if (info == null)
        continue;
    Console.WriteLine((info.Name, info.GetDescription(), info.GetAppID()));
}

/// <summary>
/// レジストリのHKEY_CLASSES_ROOT\AppIDキーに登録された実行ファイルキーの情報を取得します。
/// </summary>
sealed class AppIDKeyExeNameInfo : IDisposable
{
    private RegistryKey key;

    /// <summary>実行ファイルの名前に対応した情報を取得します。</summary>
    /// <param name="name">実行ファイルの名前。</param>
    /// <exception cref="System.ArgumentException">レジストリキーハンドルの作成に失敗。</exception>
    /// <exception cref="System.ArgumentNullException">引数namaがnull。</exception>
    public AppIDKeyExeNameInfo(string name)
    {
        if (name == null)
            throw new ArgumentNullException(nameof(name));

        using var appIdKey = OpenAppIDKey();
        key = appIdKey.OpenSubKey(name);
        if (key == null)
            throw new ArgumentException(nameof(name));
    }

    private AppIDKeyExeNameInfo(RegistryKey key)
        => this.key = key;

    /// <summary>実行ファイルの名前に対応した情報を取得します。</summary>
    /// <param name="name">実行ファイルの名前。</param>
    /// <returns>情報を取得できた場合はAppIDKeyExeNameInfoインスタンスを返します。失敗時はnull。</returns>
    [return: MaybeNull]
    public static AppIDKeyExeNameInfo Create(string name)
    {
        using var appIdKey = OpenAppIDKey();
        return (appIdKey?.OpenSubKey(name) is RegistryKey key) ? new AppIDKeyExeNameInfo(key) : null;
    }

    private static RegistryKey OpenAppIDKey()
    {
        using var hkcr = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64);
        return hkcr.OpenSubKey("AppID");
    }

    /// <summary>情報を取得するキーの名前。</summary>
    public string Name => Path.GetFileName(key.Name);

    /// <summary>AppIDキーに登録されている実行ファイルの名前一覧を取得します。</summary>
    public static string[] GetNames()
    {
        using var appIdKey = OpenAppIDKey();
        return appIdKey.GetSubKeyNames()
            .Where(name => !Guid.TryParseExact(name, "B", out _))
            .ToArray();
    }

    ~AppIDKeyExeNameInfo() => Dispose();

    /// <summary>内部のレジストリキーハンドルを解放します。</summary>
    public void Dispose()
    {
        if (key == null)
            return;
        key.Dispose();
        key = null;
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 任意の型のレジストリキーの値を取得します。
    /// </summary>
    /// <param name="name">値の名前。</param>
    /// <param name="defaultValue">値が存在しない場合の値。</param>
    /// <param name="options">オプション。</param>
    public object GetValue(
        string name,
        object defaultValue = default,
        RegistryValueOptions options = RegistryValueOptions.None)
    {
        return key.GetValue(name, defaultValue, options);
    }

    /// <summary>
    /// 特定の型のレジストリキーの値を取得します。
    /// </summary>
    /// <param name="name">値の名前。</param>
    /// <param name="defaultValue">値が存在しないか型が一致しない場合の値。</param>
    /// <param name="options">オプション。</param>
    public T GetValue<T>(
        string name,
        T defaultValue = default,
        RegistryValueOptions options = RegistryValueOptions.None)
    {
        return key.GetValue(name, defaultValue, options) is T value and T ? value : defaultValue;
    }

    /// <summary>
    /// 実行ファイルキーの概要を取得します。
    /// </summary>
    /// <returns></returns>
    public string GetDescription() => GetValue<string>("", null);

    /// <summary>
    /// 実行ファイルキーのAppIDをGuid型で取得します。
    /// </summary>
    /// <returns></returns>
    public Guid? GetAppID()
    {
        return Guid.TryParseExact(GetValue<string>("AppID", null), "B", out var guid)
            ? guid : null;
    }
}

.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Win32.Registry" Version="6.0.0-preview.2.21154.6" />
  </ItemGroup>

</Project>