windows服务启动一个当前用户的进程
首先,为什么会使用windows服务?
大多数Windows服务是以SYSTEM用户启动的。SYSTEM用户是系统中权限最高的用户,可以操作注册表中Local Machine、系统目录,不需要UAC就能以管理员权限启动一个进程等等。
在windows中,每一个用户会有一个Session ,Session 0专用于服务和其他不与用户交互的应用程序。
在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;
}
以上就是全文了~