1. 程式人生 > >windows服務啟動一個當前使用者的程序

windows服務啟動一個當前使用者的程序

windows服務啟動一個當前使用者的程序
首先,為什麼會使用windows服務? 大多數Windows服務是以SYSTEM使用者啟動的。SYSTEM使用者是系統中許可權最高的使用者,可以操作登錄檔中Local Machine、系統目錄,不需要UAC就能以管理員許可權啟動一個程序等等。
在windows中,每一個使用者會有一個Session ,Session 0專用於服務和其他不與使用者互動的應用程式。
第一個登入進來,可以進行互動式操作的使用者被連到Session 1上。第二個登入進行的使用者被分配給Session 2,以此類推。 因為Service在Session 0中,而我們登陸系統以後看到的桌面屬於另一個Session
,如果採取在服務程序中啟動子程序來顯示對話方塊,子對話方塊將無法在我們這個session中顯示。

PS: 如果Service 中想顯示MessageBox,需要使用API :WTSSendMessage
言歸正傳,那麼如何在Service為當前使用者登陸的session啟動一個程序呢?
我們可以使用API:CreateProcessAsUser 但是使用這個API需要一系列的準備 比如複製當前啟用session 使用者的Token,建立環境變數等等。

BOOL MyCreateProcessAsUser(const HANDLE& hCurrentToken,LPCTSTR strPath,LPTSTR lpCmdLine)
{
	HANDLE hTokenDup = NULL;
	if (!DuplicateTokenEx(hCurrentToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification,
		TokenPrimary, &hTokenDup))
	{
#ifdef _DEBUG
		int iError = GetLastError();
		CString strLog;
		strLog.Format(_T("DuplicateTokenEx fail,Error code:%d"), iError);
		OutputDebugString(strLog);
#endif
		return FALSE;
	}
	DWORD dwSessionID = WTSGetActiveConsoleSessionId();

	if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionID, sizeof(DWORD)))
	{
		SafeCloseHandle(hTokenDup);
		return FALSE;
	}
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = L"WinSta0\\Default";
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = HIDE_WINDOW;

	LPVOID pEnv = NULL;
	DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
	if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE))
	{
		SafeCloseHandle(hTokenDup);
		return FALSE;
	}

	if (!CreateProcessAsUser(hTokenDup, strPath, lpCmdLine, NULL, NULL, FALSE, dwCreationFlags, pEnv, NULL, &si, &pi))
	{

		SafeCloseHandle(hTokenDup);
		if (pEnv != NULL)
			DestroyEnvironmentBlock(pEnv);
		return FALSE;
	}

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	CloseHandle(hTokenDup);
	if (pEnv != NULL)
		DestroyEnvironmentBlock(pEnv);
	return TRUE;


}
這樣就可以在service中啟動一個當前使用者 許可權的程序了。
另一個問題來了,如何獲取當前啟用session 使用者的Token? WTSGetActiveConsoleSessionId();在這種情況下通常是失敗的。 一種常見的方法就是 開啟explorer.exe程序,取它的Token ...沒辦法,誰讓我service許可權高呢,咩哈哈哈
BOOL GetCurrentLogonUserToken(HANDLE& hToken)
{
	DWORD dwCurSessionId = WTSGetActiveConsoleSessionId();
	if (TRUE == WTSQueryUserToken(dwCurSessionId, &hToken))
		return TRUE;
	DWORD err = GetLastError();
	BOOL bRet = FALSE;
	HANDLE hProcessSnap = NULL;
	PROCESSENTRY32 pe32;
	DWORD dwSessionId = -1;

	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	pe32.dwSize = sizeof(PROCESSENTRY32);
	if (Process32First(hProcessSnap, &pe32))
	{
		do
		{
			if (!_tcsicmp(pe32.szExeFile, _T("explorer.exe")))
			{
				::ProcessIdToSessionId(pe32.th32ProcessID, &dwSessionId);
				if (dwSessionId != dwCurSessionId)
					continue;
				//{
				HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
				DWORD err = GetLastError();
				bRet = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
				if (bRet == 0)
				{
				}

				CloseHandle(hProcessSnap);
				return TRUE;
				//}	 
			}

		} while (Process32Next(hProcessSnap, &pe32));

		bRet = TRUE;
	}
	else
	{
		bRet = FALSE;
	}
	CloseHandle(hProcessSnap);

	return bRet;
}


以上就是全文了~