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対応時に確認する保存先と実行方針を整理する補助画面です。この記事本文では、スクリーンショットは追加せず、確認表とコード例で判断点を示します。
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で制御する
- 昇格が必要な処理だけ別プロセスにするのがベストプラクティス
