2007/02/24 My Pasokon Memo Ver. up to 1.26
2007/02/17 Auto Defrag 2000 Ver. up to 1.13
2007/02/12 Visual C++ 6.0 でのマニフェストもどきの実装
2007/02/10 Reboot File Deleter は管理者として実行させないといけない
2007/02/03 Vista の「管理者として実行」で応答不要な実装はできないのかも?
今日付けで My Pasokon Memo をバージョンアップしました。バージョンアップ内容は以下の通りです。
Ver.1.26 2007/02/24
初めて Vista 対応したソフトを公開しました。最初の対応ソフトはやはり My Pasokon Memo が基本でしょう。
今日付けで Auto Defrag 2000 をバージョンアップしました。バージョンアップ内容は以下の通りです。
Ver.1.13 2007/02/17
3番目の修正は Vista 対応です。(こういうのも Vista 対応と言うのか?!)
他のソフトの Vista 対応の時間稼ぎです。
作者は貧乏なためコンパイラーのバージョンアップをなるべく控えてきた。OSの新しい機能があればインクルードファイルを切り貼りして実現してきた。しかしマニフェストという新しい概念が Visual Studio 2005 からできたために新しいコンパイラーを買わなければならないのかと思った。しかし単純なマニフェストの機能(制限ユーザ*1で昇格プロンプト*2を出して管理者への昇格を行う)なら実現できることがわかったので、ここに公開する。
*1 せっかく Administrators に所属しているのに権限を制限される惨めなユーザ
*2 デスクトップが暗くなって、いちいち応答しなければならない、ウザいあれ
*1 *2 は一般的な言葉ではありません。
2/17 ShellExecute の引数の順序を間違えていたので修正
bool RestartAsAdminIfNeeded(LPSTR lpCmdLine, int nCmdShow); //--------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // // Adminへの昇格が必要ならリスタート if ( RestartAsAdminIfNeeded(lpCmdLine, nCmdShow) ) return 1; ・ ・ } //--------------------------------------------------------------------------- bool RestartAsAdminIfNeeded(LPSTR lpCmdLine, int nCmdShow) { // // Vista 以外の場合はそのまま OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); APICall(GetVersionEx(&os), "GetVersionEx"); if ( os.dwPlatformId != VER_PLATFORM_WIN32_NT || os.dwMajorVersion < 6 ) return false; // // 昇格しているとそのまま #define TokenElevationType (TOKEN_INFORMATION_CLASS)18 typedef enum _TOKEN_ELEVATION_TYPE { TokenElevationTypeDefault = 1, TokenElevationTypeFull, TokenElevationTypeLimited, } TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE; // HANDLE hToken = NULL; APICall(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken), "OpenProcessToken"); DWORD len; TOKEN_ELEVATION_TYPE tet; APICall(GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(TOKEN_ELEVATION_TYPE), &len), "GetTokenInformation"); APICall(CloseHandle(hToken), "CloseHandle"); if ( tet != TokenElevationTypeLimited ) return false; // // 再スタート char Self[MAX_PATH];//自分のファイル名 APICall(GetModuleFileName(NULL, Self, MAX_PATH), "GetModuleFileName"); char CurDir[MAX_PATH];//カレントディレクトリ APICall(GetCurrentDirectory(MAX_PATH, CurDir), "GetCurrentDirectory"); ShellExecute(NULL, "runas", Self, lpCmdLine, CurDir, nCmdShow); // return true; } //--------------------------------------------------------------------------- |
Windows Vista でコマンドプロンプトを立ち上げて"whoami /groups"と入力してみよう。すると以下のような結果が出る。
GROUP INFORMATION ----------------- グループ名 種類 SID 属性 ====================================== ==================== ============ ======================================== Everyone よく知られたグループ S-1-1-0 固定グループ, 既定で有効, 有効なグループ BUILTIN\Administrators エイリアス S-1-5-32-544 拒否のみに使用するグループ BUILTIN\Users エイリアス S-1-5-32-545 固定グループ, 既定で有効, 有効なグループ NT AUTHORITY\INTERACTIVE よく知られたグループ S-1-5-4 固定グループ, 既定で有効, 有効なグループ NT AUTHORITY\Authenticated Users よく知られたグループ S-1-5-11 固定グループ, 既定で有効, 有効なグループ NT AUTHORITY\This Organization よく知られたグループ S-1-5-15 固定グループ, 既定で有効, 有効なグループ LOCAL よく知られたグループ S-1-2-0 固定グループ, 既定で有効, 有効なグループ NT AUTHORITY\NTLM Authentication よく知られたグループ S-1-5-64-10 固定グループ, 既定で有効, 有効なグループ Mandatory Label\Medium Mandatory Level 不明な種類の SID S-1-16-8192 固定グループ, 既定で有効, 有効なグループ |
Reboot File Deleter ではMoveFileEx 関数を通して HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager キーの PendingFileRenameOperations にアクセスする。アクセス権限は
のように定義されているが、それが制限されてしまう。プログラム的にそのフラグを解除する方法は無さそうだ。もしそのような方法があったら、UACの意味が全くなくなってしまう(正確に言えばUAC元々意味がないと作者は思っているのだが)。
少し面倒だが、RFD.exe を右クリックして「管理者として実行(A)...」を選択すれば動きそうで、これを Vista での使用法にしてしまうかもしれない。作者が金持ちで Visual Studio 2005 を買ってマニフェストを使えばそのようなことはないのだが...
Before Power Off,Multi Function Alarm 等はDOSコマンドを指定する機能があり、指定したDOSコマンドの実行を待ったり、指定時間に起動したりします。
ニーズを考えた場合、これらのDOSコマンドはパソコンのメンテナンスを伴う物が多くなり、当然管理者権限が必要になります。更に管理者権限への昇格を求めるダイアログがいちいち出ていたのでは、それらソフトの存在意義がなくなります。
よって「管理者として実行」で応答不要な実装を考えているのですが、なかなかうまくいきません。もしかすると Vista の仕様によりUAC(User Account Control)が有効な環境ではできないのかも知れません。
// // ShellExecute ShellExecute(NULL, "runas", ..., ...); |
CreateProcess では当ダイアログが出ないのですが、ERROR_ELEVATION_REQUIRED というエラーが返されます。よって、CreateProcessAsUser あたりを使うことになりそうですが、色々調べていって以下のようなコーディングをしました。
// // OpenProcessToken HANDLE UserToken = NULL;//プライマリートークン APICall(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &UserToken), "OpenProcessToken"); // // AdminToken TOKEN_LINKED_TOKEN tlt; DWORD len; APICall(GetTokenInformation(UserToken, TokenLinkedToken, &tlt, sizeof(TOKEN_LINKED_TOKEN), &len), "GetTokenInformation"); APICall(CloseHandle(UserToken), "CloseHandle"); HANDLE AdminToken = tlt.LinkedToken;//偽装トークン // // DuplicateTokenEx HANDLE AdminToken2 = NULL; APICall(DuplicateTokenEx(AdminToken, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY, NULL, SecurityImpersonation, TokenPrimary, &AdminToken2), "DuplicateTokenEx"); APICall(CloseHandle(AdminToken), "CloseHandle"); // // STARTUPINFO STARTUPINFO stinfo; stinfo.cb = sizeof(STARTUPINFO); stinfo.lpReserved = NULL; stinfo.lpDesktop = NULL; stinfo.lpTitle = NULL; stinfo.dwFlags = 0; stinfo.cbReserved2 = 0; stinfo.lpReserved2 = NULL; stinfo.cbReserved2 = NULL; // // CreateProcessAsUser PROCESS_INFORMATION pinfo; APICall(CreateProcessAsUser(AdminToken2, NULL, "cmd.exe", NULL, NULL, FALSE, CREATE_NEW_CONSOLE|NORMAL_PRIORITY_CLASS, NULL, NULL, &stinfo, &pinfo), "CreateProcessAsUser"); APICall(CloseHandle(AdminToken2), "CloseHandle"); |
インターネットで検索するとうまくいった例もあるように思えますが、良くわかりません。なにかこの辺の事情がわかる方がいらっしゃったら教えてください。
それ以外でも Vista は問題山積です。