一、前言
自从Windows Vista后,Windows Service就不支持启动GUI application。网上很多资料介绍的方法可以启动GUI进程,但是有些不具有管理员权限,有些启动用户属于system用户,不是当前登录用户,只能通过如下方法。
二、代码
1、获取到受限的token
/* UAC开启时,当前用户拥有两个token,分别是受限的token和不受限的token。可以用下面代码获取到受限的token*/
HANDLE GetCurrentUserToken()
{
PWTS_SESSION_INFO pSessionInfo = 0;
DWORD dwCount = 0;
::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
int session_id = 0;
for (DWORD i = 0; i < dwCount; ++i)
{
WTS_SESSION_INFO si = pSessionInfo[i];
if (WTSActive == si.State)
{
session_id = si.SessionId;
break;
}
}
::WTSFreeMemory(pSessionInfo);
HANDLE current_token = 0;
BOOL bRet = ::WTSQueryUserToken(session_id, ¤t_token);
if (bRet == FALSE)
{
qCritical() << "WTSQueryUserToken failed";
return 0;
}
// 创建一个新的访问令牌来复制一个已经存在的标记
HANDLE primaryToken = 0;
bRet = ::DuplicateTokenEx(current_token, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &primaryToken);
if (bRet == FALSE)
{
qCritical() << "DuplicateTokenEx failed";
return 0;
}
::CloseHandle(current_token);
return primaryToken;
}
2、创建出具有管理员权限的GUI进程
// 创建出具有管理员权限,并且属于当前用户的界面程序
BOOL launchGUIApplication(std::wstring app, std::vector<std::wstring> params)
{
BOOL bResult = FALSE;
// 由受限的primaryToken和GetTokenInformation得到不受限的hTheToken
HANDLE primaryToken = GetCurrentUserToken();
if (primaryToken == 0)
{
qCritical() << "GetCurrentUserToken failed";
return FALSE;
}
HANDLE hTheToken = NULL;
DWORD dwSize = 0;
if ( GetTokenInformation(primaryToken, TokenLinkedToken, (VOID*)&hTheToken, sizeof(HANDLE), &dwSize))
{
// 让当前线程模拟登陆用户进行操作
if (ImpersonateLoggedOnUser(hTheToken) == TRUE)
{
DWORD dwCreationFlags = HIGH_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES Security1 = { sizeof(Security1) };
SECURITY_ATTRIBUTES Security2 = { sizeof(Security2) };
LPVOID pEnv = NULL;
if (CreateEnvironmentBlock(&pEnv, hTheToken, TRUE))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
TCHAR path[MAX_PATH];
_tcscpy_s(path, MAX_PATH, app.c_str());
TCHAR commandLine[MAX_PATH];
_tcscpy_s(commandLine, MAX_PATH, L" ");
for (auto item : params) {
_tcscat_s(commandLine, MAX_PATH, item.c_str());
}
// Launch the process in the client's logon session.
bResult = CreateProcessAsUser(
hTheToken,
(LPWSTR)(path),
(LPWSTR)(commandLine),
&Security1,
&Security2,
FALSE,
dwCreationFlags,
pEnv,
NULL,
&si,
&pi
);
if (!bResult)
{
qCritical() << "CreateProcessAsUser failed";
}
RevertToSelf();
if (pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
}
else
{
qCritical() << "ImpersonateLoggedOnUser failed";
}
CloseHandle(hTheToken);
}
CloseHandle(primaryToken);
return bResult;
}
3、调用方法
std::string strAppName = "C:\\Windows\\System32\\calc.exe";
std::wstring strAppPath = to_wchar_t(strAppName);
std::vector<std::wstring> params = {};
bool bResult = launchGUIApplication(strAppPath, params);
if (bResult == FALSE) {
qCritical() << "Failed to launch AppPath =" << strAppPath.c_str();
}