UACへの対処
Windows Vistaでは、UAC(ユーザーアカウント制御)が強化され、アプリケーションは通常、管理者権限なしで動作します。これにより、マルウェア、トロイ、ウィルス、rootキットなど脆弱性が入り込む余地を軽減します。
標準ユーザーでは、以下のような操作ができません。
- 「Program Files」フォルダ内のファイルに対する変更
- 「Windows」または「Sytem32」フォルダ内のファイルに対する変更
- 「HKLM\Software」以下にあるレジストリの変更
- ローカルコンピュータの日時の変更
- 「サービスアプリケーション」のインストールとアンインストール
といったものが挙げられます。これらは、従来も推奨されてきたことですが、Vistaからは強制的に操作できないようになっています。
Program Filesフォルダ下に設定ファイルなどを保存していたケースなどもあると思いますが、このような操作は、自動的にユーザー下のディレクトリにリダイレクトされるようになります。
UACに関連してWindows Vista向けにアプリケーションを変更するには、以下のいずれかの方針を採ります。
- UACの下でも動作するようアプリケーションを修正する
- 実行時に対処する
1の方法を採った場合、ユーザー固有のデータは、ユーザーのフォルダ/ファイル/レジストリキーに格納するようにします。具体的には、以下のいずれかに書き込みます。
- 「C:\ProgramData」か「%UserProfile%」
- レジストリ「HKCU」
ファイルの格納場所は、 SHGetFolderPath関数を使用して正しい場所を取得するようにします。以下にそれぞれの値で取得できる情報を示します。
値 | 取得できる場所 |
CSIDL_PERSONAL | My Documents |
CSIDL_APPDATA | Application Data, new for NT4 |
CSIDL_LOCAL_APPDATA | non roaming, user\Local Settings\Application Data |
CSIDL_COMMON_APPDATA | All Users\Application Data |
CSIDL_MYPICTURES | My Pictures, new for Win2K |
CSIDL_COMMON_DOCUMENTS | All Users\Documents |
次のコードは、これらの値を取得する例です。
uses SHFolder; function GetFolder(csidl: Integer; ForceFolder: Boolean = False) : string; var i: Integer; begin SetLength(Result, MAX_PATH); if ForceFolder then SHGetFolderPath(0, csidl or CSIDL_FLAG_CREATE, 0, 0, PChar(Result)) else SHGetFolderPath(0, csidl, 0, 0, PChar(Result)); i:= pos(#0, Result); if i > 0 then SetLength(Result, Pred(i)); end; function GetLocalAppDataFolder(ForceFolder: Boolean = False) : string; begin Result:= GetFolder(CSIDL_LOCAL_APPDATA, ForceFolder); end;
また、必要があるまで不用意にファイルやレジストリを書き込みモードでオープンしないことも重要です。
管理者権限が必要な操作を行いたいときには、例えば、分離した別の実行可能EXEを以下のようにして呼び出します。
procedure RunAsAdmin(hWnd: WHND; aFile: string; aParameters: string); var sei: TShellExecuteInfoA; begin FileChar(sei, SizeOf(sei), 0); sei.cbSize := SizeOf(sei); sei.Wnd := hWnd; sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI; sei.lpVerb := 'runas'; sei.lpFile := PChar(aFile); sei.lpParameters := PChar(aParameters); sei.nShow := SW_SHOWNORMAL; if not ShellExecuteEx(@sei) then RaiseLastOSError; end;