6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# - 拡張子関連付け

Posted at

はじめに

特定の拡張子ファイルを Explorer でダブルクリック、または C# の CreateProcess で対象として指定すると、対象拡張子に関連付けられたビューアなどが起動されます。
本記事では、この拡張子関連付けなどについて記載します。

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8
  • WPF - .NET Framework 4.8
  • WPF - .NET 8

記載したソースコードは .NET 8 ベースとしています。
.NET Framework 4.8 の場合は、コメントで記載している null 許容参照型の明示 ? を削除してください。

Visual Studio 2022 - .NET Framework 4.8 は、C# 7.3 が既定です。
このため、サンプルコードは、C# 7.3 機能範囲で記述しています。

表示可能なアプリ一覧

特定の拡張子ファイルを表示可能なアプリ一覧として、OpenWithProgids と OpenWithList が存在します。
Windows XP で追加された OpenWithProgids が推奨で、OpenWithList はレガシー扱いです。

OpenWithProgids

以下のレジストリを利用して、ProgId(論理名)ベースで列挙する形式です。
実行シーケンスは、HKCR\<ProgId>\shell\open\command から取得します。

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\OpenWithProgids
→ マージビュー:HKCR\<.ext>\OpenWithProgids

-- サンプル --
HKCR\.pdf\OpenWithProgids
"Acrobat.Document.DC"
"MSEdgePDF"
"ChromeHTML"

-- 実行シーケンス --
HKCR\<ProgId>\shell\open\command
↓
HKCR\MSEdgePDF\shell\open\command
@="\"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe\" --single-argument %1"

上記 @= は対象レジストリキーの (既定) 値を意味します。
regedit でエクスポート時の形式です。

OpenWithList

以下のレジストリを利用して、実行ファイル名ベースで列挙する形式です。
各キー(a, b, …)で対象アプリを管理して、MRUList でキーの順序づけされています。
実行シーケンスは HKCR\Applications\<Application>\shell\open\command から取得します。

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\OpenWithList

-- サンプル --
~\FileExts\.csv\OpenWithList
"a"="notepad.exe"
"b"="excel.exe"
"MRUList"="ba"

-- 実行シーケンス --
HKCR\Applications\<Application>\shell\open\command
↓
HKCR\Applications\notepad.exe\shell\open\command
@="%SystemRoot%\system32\NOTEPAD.EXE %1"

拡張子関連付け

特定の拡張子ファイルを Explorer でダブルクリックで起動されるアプリは、Windows 拡張子関連付けで指定されたアプリです。
拡張子関連付けは、UserChoice と HKEY_CLASSES_ROOT の2つのしくみがあり、前者が優先されます。

  • UserChoice
    • ユーザごとの選択を ProgId + Hash で管理する形態となっていて、設定アプリ以外からの手動編集を抑止
    • 「インストール時に勝手に関連付けを奪うアプリの横行」「ユーザが意図しないアプリでファイルが開かれる」ことの抑止を目的として、Windows 8 から導入
  • HKEY_CLASSES_ROOT(HKCR)
    • HKEY_LOCAL_MACHINE\Software\Classes(HKLM) と HKEY_CURRENT_USER\Software\Classes(HKCU)のマージビュー
    • HKLM を HKCU で上書きした構成(HKLM と HKCU で同一キーが存在すれば、HKCU 優先)

以降で、それぞれについて補足説明を記載します。

UserChoice

UserChoice は Windows 8 で、ユーザごとの選択を管理するために導入されました。
以下のいずれかの操作で設定の確認/変更を行うことができます。

  • 「設定」アプリ > 既定のアプリ > ファイルの種類で既定値を選択する
    settings-1.png
  • 対象拡張子のファイルのプロパティを表示。ファイルの種類で 変更...
    settings-2.png

一覧選択候補は、前述 OpenWithProgids、OpenWithList、及び、HKEY_CLASSES_ROOT の情報から作成されます。
設定内容は下記レジストリに格納されます。

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\UserChoice

-- サンプル --
~\FileExts\.txt\UserChoice
"ProgId"="AppX4ztfk9wxr86nxmzzq47px0nh0e58b8fw"
"Hash"="ZUwGhsJ3ZWE="
値名 内容
ProgId 関連付けられたアプリの論理名
Hash 整合性チェック用の Windows 生成ハッシュ(編集不可)
  • 他の関連付け設定より 最優先 される
  • Hash が一致しない場合は無効とされる
  • UserChoice と同一階層に UserChoiceLatest キーも存在
  • UserChoice、UserChoiceLatest どちらとも、アクセス制限(アクセス許可で「拒否」エントリが存在)されているため、この状態では regedit などから削除できません

実行シーケンスは、前述 OpenWithProgids と同じく、<ProgId> をキーとして shell\open\command の (既定値) を利用します。

-- 実行シーケンス --
HKCR\<ProgId>\shell\open\command
↓
HKCR\AppX4ztfk9wxr86nxmzzq47px0nh0e58b8fw\shell\open\command
@="\"C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2504.62.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe\""

UserChoice は、ユーザの意図を明確に反映し、セキュリティと整合性を保つことを目的としています。
UserChoice が用意された経緯を以下に記載します。

  • ユーザーの選択を尊重するため
    • 従来の Windows では、アプリケーションがレジストリを直接書き換えることで、勝手に拡張子の関連付けを変更できてしまいました。これによって「インストール時に勝手に関連付けを奪うアプリの横行」「ユーザーが意図しないアプリでファイルが開かれる」ということが多発していました。
  • セキュリティ強化のため
    • UserChoice キーには、Windows 設定ツールで設定したことを検証するための Hash 値が用意されています。これによって「レジストリ直接編集の無効化」「信頼できる UI 操作を通じてのみ変更が反映」を実現しています。
  • 一貫性のある動作を保証するため
    • 複数のアプリが同じ拡張子を主張する場合でも、UserChoice によって「現在のユーザが選んだアプリ」が明確に記録されるため、OS の挙動が安定します。

OpenWith

「このファイルを開くアプリを選んでください」ダイアログを表示するためのシステムコンポーネント OpenWith.exe が存在します。

System.Diagnostics.Process.Start("openwith.exe", @"C:\Path\To\sample.pdf");

詳細な仕様は公開されておらず、Windows 11 22H2 で上記シーケンスで起動すると「規定値を設定する」ボタンでなく「一度だけ」ボタンとなり、設定を変更することはできませんでした。

openwith-1.png

ファイルプロパティ

対象拡張子のファイルのプロパティを表示。ファイルの種類で 変更... というアプローチで C# 実装を考えてみます。

settings-2.png

ファイルプロパティ表示は、下記手法などで可能です。

  • WIN32API ShellExecuteEx で lpVerb = "properties" 指定
  • WIN32API SHObjectProperties を利用

後者のサンプルコードを記載します。

// プロパティダイアログ表示
private static bool ShowProperties(string filePath)
{
  bool result = NativeMethods.SHObjectProperties(
                   IntPtr.Zero, NativeMethods.SHOP_FILEPATH, filePath, null);
  return result;
}
// WIN32API
private static class NativeMethods
{
  // .NETFramework の場合、引数は「string pszPropertyPage」で良い
  [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
  public static extern bool SHObjectProperties(IntPtr hwnd, uint shopObjectType, 
                                    string pszObjectName, string? pszPropertyPage);

  public const uint SHOP_FILEPATH = 0x2;
}

WIN32API を直接記述していますが、Microsoft が提供している CsWin32 を利用するという手もあります。

完全に自動化するには、自動的に「変更...」をクリック、もしくは、ALT + C キーコード送信などの処理が必要となります。
C# + UIAutomation 利用などの手法が考えられますが、Windows OS バージョンに依存したダイアログキャプションなどの変化などについて考慮が必要となります。

今回のサンプルとしては、プロパティダイアログ表示止まりとして、「変更...」ボタン以降の操作はユーザに委ねることとします。
(拡張子関連付けを変更する場合は、以降のダイアログで「変更...」ボタンを選択してくださいなどのガイドメッセージで対処する形態です)

HKEY_CLASSES_ROOT

HKEY_LOCAL_MACHINE と HKEY_CURRENT_USER のマージビュー HKEY_CLASSES_ROOT は、Windows XP で確立され、現在も利用されています。

  • HKEY_LOCAL_MACHINE\Software\Classes(HKLM)
    • 「システム全体に適用される既定の関連付け」を定義する場所です
    • 対象範囲:すべてのユーザーに共通
    • アプリケーションのインストーラーが .txt や .html などの拡張子に対して、どのアプリケーションを使うかを登録
    • COM クラス(ProgID、CLSID、AppID など)の登録
    • 書き込みには管理者権限が必要
    • ユーザーが個別に変更した設定(HKCU)に上書きされる可能性がある
  • HKEY_CURRENT_USER\Software\Classes(HKCU)
    • 「現在ログオン中のユーザーに限定された関連付け」を定義します。
    • 対象範囲:現在のユーザーのみ
    • ユーザーが「プログラムから開く」で選んだアプリケーションの記録
    • HKLM よりも 優先される
    • HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts 以下に UserChoice や OpenWithList などの詳細設定が保存される
    • UserChoice の ProgId と Hash によって、ユーザーが選んだアプリが強制的に適用される

HKEY_CLASSES_ROOT は、以下のルールで HKLM と HKCU を統合して構成されます。

  • HKCU\Software\Classes に存在するキーはすべて表示される
  • HKLM\Software\Classes に存在し、HKCU に同名キーがないものだけが追加される
  • 同名キーが両方にある場合は、HKCU 側の内容が優先される。つまり、ユーザー設定がシステム設定を上書きする構造です
  • 例えば .txt の関連付けは、HKCU\Software\Classes.txt が存在すれば、それが優先されて HKCR に表示され、存在しなければ、HKLM\Software\Classes.txt の内容が表示される
  • 書き込み時の挙動は、HKCR に直接書き込むと、実際には HKLM\Software\Classes に保存されます。ユーザ固有の設定を行いたい場合は、明示的に HKCU\Software\Classes に書き込む必要があります。

基本的な構造

対象拡張子のレジストリキー既定値が ProgId となっています。

-- サンプル --
HKCR\.pdf
@="Acrobat.Document.DC"

-- 実行シーケンス --
HKCR\<ProgId>\shell\open\command
↓
HKCR\Acrobat.Document.DC\shell\open\command
@="\"C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.exe\" \"%1\""

HKCR\<ProgId> についての補足情報です。

  • HKCR\<ProgId> の既定値に、対象拡張子ファイルの説明があります(例:Adobe Acroba 文書)
  • HKCR\<ProgId>\DefaultIcon の既定値にアイコン情報があります
  • HKCR\<ProgId>\shell 下には open と同一階層で、print(印刷)などが存在します
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?