カタバミさんのプログラミングノート

日曜プログラマーがプログラミング関係のメモを記録するブログです。

C#で実行ファイルの会社名にMicrosoftを含むレジストリに登録されたCOMクラスを選択する方法

C#Windows 10でレジストリに登録されたCOMクラスから実行ファイルの会社名にMicrosoftを含むものを選択するソースコードです。

概要

レジストリのアクセスにはMicrosoft.Win32名前空間のRegistryKeyクラス、実行ファイル(exe、dll)のバージョン情報取得にはSystem.Diagnostics名前空間のFileVersionInfoクラスを使用することができます。

ソースコード

// 参照:System 4.0.0, System.Core 4.0.0
// 環境:.NET Framework 4.7.2, C# 7.2

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace ConsoleApp1
{
    static class Program
    {
        static void Main()
        {
            // レジストリに登録されたCOMクラスのProgIDとCLSID列挙
            var progIdAndCLSIDs = GetRegistedProgIDAndCLSID(
                RegistryView.Registry64);

            // CLSIDの情報取得
            var clsidInfos = default(IEnumerable<CLSIDInfo>);
            using (var classesRootKey = RegistryKey.OpenBaseKey(
                RegistryHive.ClassesRoot, RegistryView.Registry64))
            {
                clsidInfos = progIdAndCLSIDs.Values.Select(
                    clsid => CLSIDInfo.Get(clsid, classesRootKey));
            }

            // CLSID情報→FileVersionInfo
            var inProcServer32ClsidInfos = clsidInfos
                .Where(clsidInfo => clsidInfo.InProcServer32 != null);
            var clsidIdAndFileVerInfos = inProcServer32ClsidInfos
                .Select(clsidInfo =>
                {
                    try
                    {
                        return (
                            CLSIDInfo: clsidInfo,
                            FileVerInfo: FileVersionInfo.GetVersionInfo(
                                clsidInfo.InProcServer32));
                    }
                    catch
                    {
                        return (
                            CLSIDInfo: clsidInfo,
                            FileVerInfo: null);
                    }
                })
                .ToArray(); //評価実行

            // FileVersionInfo.CompanyNameにMicrosoftが含まれる要素の抽出
            var microsoftClsidIdAndFileVerInfos = clsidIdAndFileVerInfos
                .Where(info =>
                {
                    try
                    {
                        return info.FileVerInfo.CompanyName
                        .Contains("Microsoft");
                    }
                    catch
                    {
                        return false;
                    }
                })
                .ToArray();

#if true
            // 概要のCSV出力(強制上書き)
            using (var fo = File.OpenWrite("result.csv"))
            {
                var writer = new StreamWriter(fo, Encoding.Unicode);
                foreach (var info in microsoftClsidIdAndFileVerInfos)
                {
                    writer.Write($"{info.CLSIDInfo.Description?.Replace('\t', ' ')}");
                    writer.Write("\t");
                    writer.Write($"{info.CLSIDInfo.ProgID?.Replace('\t', ' ')}");
                    writer.Write(",");
                    writer.Write($"{info.FileVerInfo.FileDescription?.Replace('\t', ' ')}");
                    writer.WriteLine();
                }
            }
#else
            // 概要の文字列変換(デバッガーで確認用)
            var result = string.Join<string>(
                Environment.NewLine,
                microsoftClsidIdAndFileVerInfos.Select(
                    (info, i) => $"{i:0000}:{info.CLSIDInfo.Description}-{info.CLSIDInfo.ProgID}-{info.FileVerInfo.FileDescription}"));
#endif
        }

        [DebuggerDisplay("{Description}-{CLSID}")]
        public sealed class CLSIDInfo
        {
            #region コンストラクタ
            private CLSIDInfo() { }
            public static CLSIDInfo Get(string clsid, RegistryView view)
            {
                // 不正な文字列対策のため、意図的にGUIDへの変換を試みます。
                return Get(Guid.ParseExact(clsid, "B"), view);
            }
            public static CLSIDInfo Get(Guid guid, RegistryView view)
            {
                using (var key = RegistryKey.OpenBaseKey(
                    RegistryHive.ClassesRoot,
                    view))
                {
                    var info = new CLSIDInfo();
                    info.Parse(guid, key);
                    return info;
                }
            }
            public static CLSIDInfo Get(string clsid, RegistryKey classesRootKey)
            {
                // 不正な文字列対策のため、意図的にGUIDへの変換を試みます。
                return Get(Guid.ParseExact(clsid, "B"), classesRootKey);
            }
            public static CLSIDInfo Get(Guid guid, RegistryKey classesRootKey)
            {
                var info = new CLSIDInfo();
                info.Parse(guid, classesRootKey);
                return info;
            }
            #endregion

            public string CLSID { get; private set; }
            public string Description { get; private set; }
            public string InProcServer32 { get; private set; }
            public string InProcServer32ThreadingModel { get; private set; }
            public string ProgID { get; private set; }
            public string Version { get; private set; }
            public string VersionIndependentProgID { get; private set; }

            private void Parse(Guid guid, RegistryKey classsessRootKey)
            {
                CLSID = guid.ToString("B");
                using (var clsidKey = classsessRootKey.OpenSubKey(
                    "CLSID\\" + CLSID, false))
                {
                    if (clsidKey == null)
                        return;
                    Description = (string)clsidKey.GetValue(null);
                    using (var key = clsidKey.OpenSubKey("InProcServer32", false))
                    {
                        InProcServer32 = (string)key?.GetValue(null);
                        InProcServer32ThreadingModel = (string)key?.GetValue("ThreadingModel");
                    }
                    using (var key = clsidKey.OpenSubKey("ProgID", false))
                    {
                        ProgID = (string)key?.GetValue(null);
                    }
                    using (var key = clsidKey.OpenSubKey("Version", false))
                    {
                        Version = (string)key?.GetValue(null);
                    }
                    using (var key = clsidKey.OpenSubKey("VersionIndependentProgID", false))
                    {
                        VersionIndependentProgID = (string)key?.GetValue(null);
                    }
                }
            }
        }

        public static IDictionary<string, string> GetRegistedProgIDAndCLSID(
            RegistryView view)
        {
            var pairs = new Dictionary<string, string>();
            using (var classesRootKey = RegistryKey.OpenBaseKey(
                RegistryHive.ClassesRoot, view))
            {
                foreach (var name in classesRootKey.GetSubKeyNames())
                {
                    using (var key = classesRootKey.OpenSubKey(
                        name + @"\CLSID", false))
                    {
                        var value = key?.GetValue(null);
                        if (!(value is null))
                        {
                            pairs.Add(name, (string)value);
                        }
                    }
                }
                return pairs;
            }
        }
    }
}

出力例(抜粋)

TaskScheduler class	Schedule.Service.1,Task Scheduler COM API
TaskScheduler class	Schedule.Service.1,Task Scheduler COM API
Moniker to a Windows Script Component	script,Windows ® Script Component Runtime
Microsoft Scriptlet Component	ScriptBridge.ScriptBridge.1,Microsoft(R) HTML ビューアー
CScriptedDiag	ScriptedDiag.Engine.1,スクリプト化された診断の実行エンジン
CScriptedDiag	ScriptedDiag.Engine.1,スクリプト化された診断の実行エンジン
Scripting.Dictionary	Scripting.Dictionary,Microsoft ® Script Runtime
Script Encoder Object	Scripting.Encoder,Microsoft ® Script Runtime
FileSystem Object	Scripting.FileSystemObject,Microsoft ® Script Runtime
Scripting.Signer	Scripting.Signer,Microsoft ® Shell Extension for Windows Script Host
Moniker to a Windows Script Component	script,Windows ® Script Component Runtime
Constructor that allows hosts better control creating scriptlets	Scriptlet.Constructor,Windows ® Script Component Runtime
Object under which scriptlets may be created	Scriptlet.Context,Windows ® Script Component Runtime
Factory bindable using IPersistMoniker	Scriptlet.Factory,Windows ® Script Component Runtime
Object for encoding scriptlets	Scriptlet.HostEncode,Windows ® Script Component Runtime
Object for constructing type libraries for scriptlets	Scriptlet.TypeLib,Windows ® Script Component Runtime
Factory bindable using IPersistMoniker	Scriptlet.Factory,Windows ® Script Component Runtime
Constructor for Scriptlet ASP Handler	ScriptletHandler.ASP,Windows ® Script Component Runtime
Constructor for Scriptlet Automation Handler	ScriptletHandler.Automation,Windows ® Script Component Runtime
Constructor for Scriptlet Behavior Handler	ScriptletHandler.Behavior,Windows ® Script Component Runtime
Constructor for Scriptlet Event Handler	ScriptletHandler.Event,Windows ® Script Component Runtime
ScriptoSys Class	ScriptoSys.Scripto.1,Microsoft ScriptO
App Content Filter	Search.AppContentFilter.1,Microsoft WinRT ストレージ API
App Content Filter	Search.AppContentFilter.1,Microsoft WinRT ストレージ API
Windows Search Data Source	Search.CollatorDSO.1,Microsoft Tripoli Query
Windows Search Data Source	Search.CollatorDSO.1,Microsoft Tripoli Query
Search command creator object	Search.CommandCreator.1,Microsoft Tripoli Query
Search command creator object	Search.CommandCreator.1,Microsoft Tripoli Query
Windows Search Service Client Side Cache Protocol Handler	Search.CscHandler.1,MSSearch Vista プラットフォーム
Windows Search Service Client Side Cache Protocol Handler	Search.CscHandler.1,MSSearch Vista プラットフォーム

補足説明

COMクラスの登録場所

COMクラスの情報はHKCRキー\<ProgID>キーおよびHKCR\CLSID\<CLSID>キーに分散して登録されています。ProgIDはCOMクラスのユーザーが利用しやすい名前であり、<ProgID>キーはCLSIDサブキーの既定値に詳細情報の登録されるCLSIDを持ちます。<CLSID>キーはCOMクラスの実行ファイルの場所等の詳細な情報を持ちます。

64ビットOSではWOW64が有効になることに注意してください。

レジストリとWOW64

HKCR(HKEY_CLASSESS_ROOT)へのアクセス時、RegistryKey.OpenBaseKey関数でRegistryView.Registry64を指定しないとWOW64(32ビット環境、Wow6432Nodeキー配下キーへのリダイレクト)の影響を受けることに注意してください。

なお、32ビットOSでRegistryView.Registry64を指定した場合、自動的に32ビットビューが使用されます(RegistryView Enum (Microsoft.Win32) | Microsoft Docs)。

名前付きタプル

C# 7.2では以下の形式で名前付きタプルを作成することができます。詳細はタプル型 - C# ガイド | Microsoft Docsをご覧ください。

(Name1: value1, Name2: value2)

インデックス付きSelect

LINQのSelectメソッドは二番目の引数を与えることで0ベースのインデックスを使用することができます。

// "あ0","い1","う2"
var ss = new[]{"あ","い","う"}.Select((s, i) => s+i);