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);
}
}
}