Windows 服務在系統啟動時即於背景執行,無需使用者登入。部分服務嘗試顯示 UI 對話框或與使用者應用程式通訊,在 Windows 7 (含 Vista) 的 Session 0 隔離 架構下將面臨相容性問題:服務運行於隔離的 Session 0,無法直接存取使用者互動桌面 (Session 1+)。若不處理安全性與通訊機制,服務將無法正常運作。
Session0_Starter 方案,建置 (注意 Debug/Release、x86/x64)。sc create TimeService binPath= C:\Session0_Starter\Debug\TimeService.exe (路徑依實際輸出調整,binPath= 後須有空格)。services.msc 開啟服務管理單元,啟動 TimeService。

若服務僅需簡單通知使用者,可用 WTSSendMessage 直接在使用者桌面顯示訊息方塊,避免互動式服務偵測彈窗。
UI\Native\TimeService.cpp,找到第一個 //TODO,替換 MessageBox:LPWSTR lpszTitle = L"Time Change";
LPWSTR lpszText = L"Notification: 5 seconds have elapsed.\r\nWould you like to see more details?";
DWORD dwSession = WTSGetActiveConsoleSessionId();
WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSession, lpszTitle,
static_cast<DWORD>((wcslen(lpszTitle) + 1) * sizeof(wchar_t)),
lpszText, static_cast<DWORD>((wcslen(lpszText) + 1) * sizeof(wchar_t)),
MB_YESNO | MB_ICONINFORMATION, 0 /* 無限等待 */, &dwResponse, TRUE);

若服務需豐富 UI (非簡單訊息方塊),應在使用者 Session 啟動獨立進程 (TimeServiceClient.exe) 顯示 UI,再透過 IPC 與服務通訊。
TimeService.cpp 第二個 //TODO 區塊,完整步驟:// 1. 取得作用中主控台 Session ID 與使用者 Token
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
HANDLE hToken = NULL;
if (!WTSQueryUserToken(dwSessionID, &hToken)) goto Cleanup;
// 2. 複製 Token 以建立進程
HANDLE hDuplicatedToken = NULL;
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken)) goto Cleanup;
// 3. 建立環境區塊
LPVOID lpEnvironment = NULL;
if (!CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE)) goto Cleanup;
// 4. 取得用戶端執行檔完整路徑 (同目錄下 TimeServiceClient.exe)
WCHAR lpszClientPath[MAX_PATH];
GetModuleFileName(NULL, lpszClientPath, MAX_PATH);
PathRemoveFileSpec(lpszClientPath);
wcscat_s(lpszClientPath, L"\\TimeServiceClient.exe");
// 5. 以使用者身分建立進程
STARTUPINFO si = {0}; si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
if (!CreateProcessAsUser(hDuplicatedToken, lpszClientPath, NULL, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
lpEnvironment, NULL, &si, &pi)) goto Cleanup;
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
bSuccess = TRUE;
Cleanup:
if (!bSuccess) ShowMessage(L"An error occurred while creating fancy client UI", L"Error");
if (hToken) CloseHandle(hToken);
if (hDuplicatedToken) CloseHandle(hDuplicatedToken);
if (lpEnvironment) DestroyEnvironmentBlock(lpEnvironment);
if (g_hAlertEvent) CloseHandle(g_hAlertEvent);
安全警示: 範例為教學簡化,未遵循所有安全程式碼標準。實務中跨 Session 建立進程、共享 Kernel 物件、IPC 通訊,必須進行威脅建模與安全性審查,設定最嚴格的 DACL/SACL,避免提權攻擊面。

AlertService:sc create AlertService binPath= C:\Session0_Starter\Debug\AlertService.exeAlertService。AlertServiceClient —— 錯誤 2 (找不到檔案/事件),因服務建立事件於 Session 0 命名空間,客戶端於 Session 1 無法看見。Security\Native\AlertService.cpp //TODO STEP 1:g_hAlertEvent = CreateEvent(NULL, FALSE, FALSE, L"Global\\AlertServiceEvent");AlertServiceClient.cpp //TODO STEP 1:HANDLE hEvent = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\AlertServiceEvent");AlertService.cpp //TODO STEP 2 完整流程:// 1. 取得當前使用者 Token 與 SID
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
HANDLE hToken = NULL;
if (!WTSQueryUserToken(dwSessionID, &hToken)) goto Cleanup;
DWORD dwLength;
TOKEN_USER* account = NULL;
if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto Cleanup;
account = (TOKEN_USER*)new BYTE[dwLength];
if (!GetTokenInformation(hToken, TokenUser, account, dwLength, &dwLength)) goto Cleanup;
// 2. SID 轉字串並建構 SDDL
LPWSTR lpszSid = NULL;
if (!ConvertSidToStringSid(account->User.Sid, &lpszSid)) goto Cleanup;
WCHAR sddl[1000];
wsprintf(sddl, L"O:SYG:BAD:(A;;GA;;;SY)(A;;GA;;;%s)S:(ML;;NW;;;ME)", lpszSid);
// O:SY (擁有者 SYSTEM) G:BAD (主要群組 Built-in Administrators)
// DACL: (A;;GA;;;SY) 允許 SYSTEM 完整存取, (A;;GA;;;%s) 允許當前使用者完整存取
// SACL: (ML;;NW;;;ME) 強制完整性 Medium (允許 Medium 完整性以上進程寫入)
// 3. SDDL 轉安全描述子並建立事件
PSECURITY_DESCRIPTOR sd = NULL;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1, &sd, NULL)) goto Cleanup;
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = sd;
sa.bInheritHandle = FALSE;
g_hAlertEvent = CreateEvent(&sa, FALSE, FALSE, L"Global\\AlertServiceEvent");
if (!g_hAlertEvent) goto Cleanup;
Cleanup:
if (hToken) CloseHandle(hToken);
if (account) delete[] account;
if (lpszSid) LocalFree(lpszSid);
if (sd) LocalFree(sd);
if (!g_hAlertEvent) CloseHandle(g_hAlertEvent);
AlertService,Ctrl+H 開啟控制代碼視圖。\BaseNamedObjects\AlertServiceEvent,右鍵 Properties > Security —— 可見允許存取的群組包含 SYSTEM、Administrators、當前使用者。

注意: 範例 SDDL 非最佳實務。實務中必須依最小權限原則設定 DACL/SACL,並完成威脅建模,避免建立安全漏洞。
installutil LogService.exe。LoggingService,隨後停止。C:\LogService.txt,嘗試刪除 —— 拒絕存取,因檔案由 SYSTEM 建立,預設完整性層級為 System,標準使用者 (Medium) 無法寫入/刪除。Security\Managed\LoggingService.cs //TODO:IntegrityLevelHelper.SetFileIntegrityLevel(@"C:\LogService.txt", IntegrityLevel.Medium);
本實驗完整展示 Session 0 隔離導致的三大問題及解決方案:
WTSSendMessage (簡易通知) 或 使用者 Session 啟動獨立 UI 進程 (豐富介面)。核心原則:服務運行於 Session 0,不直接觸碰使用者桌面;跨 Session 共享資源必須顯式設定命名空間與安全描述子;遵循最小權限原則,完成威脅建模。