Windows VISTA以降のWindowsではセッション0分離の機能により、サービスから直接UIを表示することができなくなりました。Microsoftの互換性情報では代替手段として「ユーザーログイン時に常駐プロセスを起動しサービスと通信を行う。」「WTSSendMessage関数を使用してメッセージボックス相当の表示を行う。」「CreateProcessAsUserを使用してログインユーザー権限でアプリケーションを起動する。」の三つの方法が提示されています。ここでは最後の「CreateProcessAsUserを使用してログインユーザー権限でアプリケーションを起動する。」サンプルを提示します。
サービスから画面を表示するにはRemote Desktop Services APIを使用します。
WTSGetActiveConsoleSessionId関数で物理コンソールのセッションIDを取得します。
WTSQueryUserToken関数で取得したセッションIDのユーザートークンを取得します。
DuplicateTokenEx関数で取得したユーザートークンを複製し、プライマリトークンを作成します。
CreateEnvironmentBlockを使用してユーザーの環境変数を取得します。
CreateProcessAsUser関数を使用して画面を表示するプロセスを起動します。
BOOL bRet(FALSE); HANDLE hUserToken(INVALID_HANDLE_VALUE); if (::WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hUserToken)) { SECURITY_ATTRIBUTES sa; memset(&sa, 0x00, sizeof(sa)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); HANDLE hDupedToken(INVALID_HANDLE_VALUE); if (::DuplicateTokenEx(hUserToken, 0, &sa, SecurityIdentification, TokenPrimary, &hDupedToken)) { PVOID lpEnvironment(NULL); if (::CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE)) { STARTUPINFO stStartUpInfo; memset(&stStartUpInfo, 0, sizeof(STARTUPINFO)); stStartUpInfo.cb = sizeof(STARTUPINFO); stStartUpInfo.lpDesktop = _T("winsta0¥¥default"); PROCESS_INFORMATION ProcessInfo; memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION)); ::CreateProcessAsUser(hDupedToken, _T(""), (char*)(const char*)strCmd, NULL, NULL , FALSE, CREATE_UNICODE_ENVIRONMENT, lpEnvironment, NULL, &stStartUpInfo, &ProcessInfo); } } }