Windows Vista 以降の UAC(ユーザーアカウント制御)により、Program Files フォルダへの書き込みが制限されるようになりました。古い MFC アプリケーションでは、設定ファイルやログを EXE と同じフォルダに書いていたために動作しなくなるケースがあります。
この記事では、MFC アプリのUAC 対応の要点を解説しています。
まず確認するもの
UAC 対応では、最初に「どこへ書き込んでいるか」と「本当に管理者権限が必要か」を分けて確認します。設定ファイルやログの保存だけなら、管理者権限を要求する前に、ユーザーごとの書き込み可能な場所へ移すのが基本です。
| 確認するもの | 見る理由 |
|---|---|
| 保存先パス | Program Files や Windows 配下へ書いていないか確認する |
| 実行権限 | 通常ユーザーで動かす処理か、昇格が必要な処理かを分ける |
| マニフェスト | asInvoker を基本にし、必要な場合だけ昇格を検討する |
| VirtualStore | 古いアプリの書き込みが別の場所へリダイレクトされていないか確認する |
UAC による影響
| 場所 | 標準ユーザーでの書き込み |
|---|---|
C:\Program Files\ | × 拒否される |
C:\Windows\ | × 拒否される |
HKEY_LOCAL_MACHINE | × 拒否される |
%APPDATA%(ユーザーフォルダ) | ○ 許可 |
%LOCALAPPDATA% | ○ 許可 |
古いアプリで多い失敗は、設定ファイルやログを Program Files 配下へ直接書こうとする設計です。UAC 対応では、まず通常実行のまま書き込める保存先へ移すことを考えます。
付属サンプルは、UAC プロンプトや Program Files への書き込み失敗を強制的に再現するものではありません。環境差の大きい挙動を無理に再現するのではなく、UAC 対応時に確認する保存先と実行方針を 1 画面で整理するチェック画面として用意しています。スクリーンショットを撮影する場合は、「現在の権限」「Program Files に書く旧設計」「APPDATA / LOCALAPPDATA の推奨保存先」「asInvoker / runas の方針」が同時に見える状態を使います。
VirtualStore が起こす保存先のずれ
UAC 非対応のアプリが Program Files に書き込もうとすると、Windows が %LOCALAPPDATA%\VirtualStore\ にリダイレクトすることがあります。その結果、アプリ側は書き込めたように見えても、実際の保存先が想定とずれることがあります。
対策 1: ファイルパスを適切な場所に変更
// ユーザーごとの設定ファイル
CString GetSettingsPath()
{
TCHAR szPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, szPath);
CString strPath(szPath);
strPath += _T("\\MyApp\\settings.ini");
return strPath;
}
// ローカルデータ(キャッシュなど)
CString GetLocalDataPath()
{
TCHAR szPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, szPath);
CString strPath(szPath);
strPath += _T("\\MyApp\\");
// フォルダが無ければ作成
CreateDirectory(strPath, NULL);
return strPath;
}
保守案件では既存コードとの互換性から SHGetFolderPath を見かけることがあります。新規実装や大きめの改修では、可能であれば SHGetKnownFolderPath と FOLDERID_RoamingAppData / FOLDERID_LocalAppData を使う形へ寄せると、現在の Windows API としては自然です。
対策 2: マニフェストでの権限指定
どうしても管理者権限が必要な場合は、マニフェストで昇格を要求できます。
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!-- asInvoker: 呼び出し元と同じ権限(推奨) -->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<!-- requireAdministrator: 管理者権限を要求 -->
<!-- <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> -->
</requestedPrivileges>
</security>
</trustInfo>
requireAdministrator にすると起動時に必ず UAC ダイアログが表示されます。設定ファイルの書き込みだけのために昇格を要求するのは避けてください。
対策 3: 部分的な昇格
管理者権限が必要な処理だけ別プロセスとして起動する方法もあります。
// 管理者権限で別の EXE を起動
ShellExecute(m_hWnd, _T("runas"), _T("MyHelper.exe"),
_T("/install"), nullptr, SW_SHOWNORMAL);
この形なら、普段の設定保存やログ出力は通常権限のまま動かし、インストールやサービス登録など本当に昇格が必要な処理だけを分離できます。
まとめ
- Program Files への書き込みは標準ユーザーでは拒否される
- 設定ファイルやログは
%APPDATA%や%LOCALAPPDATA%に保存する - 管理者権限はマニフェストの
requestedExecutionLevelで制御する - 昇格が必要な処理だけ別プロセスにするのがベストプラクティス
