windows-server-2012 - 无法使 Windows 服务在 Windows Server 2012 上正确调用子进程
问题描述
我编写的以下函数应该由 Windows 服务调用以启动子进程(作为用户),因此子进程可以与用户交互,即具有 UI。它在 Windows 桌面上完美运行,但在 Windows Server 2012 上无法正常运行。UI 未显示(甚至不是控制台窗口),并且我们为卸载服务而注册的热键也无法正常工作。
以下代码:
void WINAPI Run(DWORD dwTargetSessionId, int desktop, LPTSTR lpszCmdLine)
{
wprintf(_T("Run client start"));
if (hPrevAppProcess != NULL)
{
TerminateProcess(hPrevAppProcess, 0);
WaitForSingleObject(hPrevAppProcess, INFINITE);
}
HANDLE hToken = 0;
WTS_SESSION_INFO *si;
DWORD cnt = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &si, &cnt);
for (int i = 0; i < (int)cnt; i++)
{
if (si[i].SessionId == 0)continue;
wprintf(_T("Trying session id %i (%s) user admin token"), si[i].SessionId, si[i].pWinStationName);
HANDLE userToken;
if (WTSQueryUserToken(si[i].SessionId, &userToken))
{
wprintf(_T("WTSQueryUserToken succeced"));
TOKEN_LINKED_TOKEN admin;
DWORD len;
if (GetTokenInformation(userToken, TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &len))
{
wprintf(_T("Success using user admin token"));
hToken = admin.LinkedToken;
break;
}
else
wprintf(L"GetTokenInformation() failed");
CloseHandle(userToken);
}
else
{
DWORD error = GetLastError();
if (error)
{
LPVOID lpMsgBuf;
DWORD bufLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
utils::WriteLogFile(L"Error '%s'\n", lpMsgBuf);
}
}
}
if (hToken == 0)
{
HANDLE systemToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &systemToken);
DuplicateTokenEx(systemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hToken);
CloseHandle(systemToken);
int i;
for (i = 0; i < (int)cnt; i++)
{
if (si[i].SessionId == 0)continue;
if (SetTokenInformation(hToken, TokenSessionId, &si[i].SessionId, sizeof(DWORD)))
{
wprintf(_T("Success using system token with set user session id %i"), si[i].SessionId);
break;
}
}
if (i == cnt)
wprintf(_T("No success to get user admin token nor system token with set user session id"));
}
WTSFreeMemory(si);
STARTUPINFO startupInfo = {};
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.lpDesktop = _T("winsta0\\default");
LPVOID pEnv = NULL;
CreateEnvironmentBlock(&pEnv, hToken, TRUE);
PROCESS_INFORMATION processInfo = {};
PROCESS_INFORMATION processInfo32 = {};
TCHAR szCurModule[MAX_PATH] = { 0 };
GetModuleFileName(NULL, szCurModule, MAX_PATH);
BOOL bRes = FALSE;
std::wstring commandLine;
commandLine.reserve(1024);
commandLine += L"\"";
commandLine += szCurModule;
commandLine += L"\" \"";
commandLine += SERVICE_COMMAND_LUNCHER;
commandLine += L"\"";
wprintf(_T("launch SG_WinService with CreateProcessAsUser ... %s"), commandLine.c_str());
bRes = CreateProcessAsUserW(hToken, NULL, &commandLine[0], NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE, pEnv,
NULL, &startupInfo, &processInfo);
if (bRes == FALSE)
{
DWORD dwLastError = ::GetLastError();
TCHAR lpBuffer[256] = _T("?");
if (dwLastError != 0) // Don't want to see a "operation done successfully" error ;-)
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, // It´s a system error
NULL, // No string to be formatted needed
dwLastError, // Hey Windows: Please explain this error!
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Do it in the standard language
lpBuffer, // Put the message here
255, // Number of bytes to store the message
NULL);
(_T("CreateProcessAsUser(SG_WinService) failed - Error : %s (%i)"), lpBuffer, dwLastError);
}
else
{
wprintf(_T("CreateProcessAsUser(SG_WinService) success. New process ID: %i"), processInfo.dwProcessId);
}
}
返回以下输出:
05.06.2019 04:25 5856:尝试会话 id 1(控制台)用户管理令牌
05.06.2019 04:25 5856:尝试会话 id 2 (RDP-Tcp#27) 用户管理令牌
05.06.2019 04:25 5856:尝试会话 id 65536 (RDP-Tcp) 用户管理令牌
05.06.2019 04:25 5856:成功使用系统令牌和设置的用户会话 id 1
05.06.2019 04:25 5856:使用 CreateProcessAsUser 启动 SG_WinService ...“C:\myservice\SG_WinService.exe”“ServiceIsLuncher”
05.06.2019 04:25 5856: CreateProcessAsUser(SG_WinService) 成功。新进程 ID:5580
更新:添加对 GetLastError() 的调用后,我得到:
09.06.2019 04:55 6008:尝试会话 id 1 用户管理令牌
09.06.2019 04:55 6008:错误“尝试引用不存在的令牌。
09.06.2019 04:55 6008:尝试会话 id 3 用户管理令牌
09.06.2019 04:55 6008:尝试会话 id 65536 用户管理令牌
09.06.2019 04:55 6008:错误“系统找不到指定的文件。
09.06.2019 04:55 6008:成功使用系统令牌和设置的用户会话 id 1
09.06.2019 04:55 6008:使用 CreateProcessAsUser 启动 SG_WinService ...
"C:\Users\Administrator\Desktop\src\SpyfiWebApp\SG_WinService.exe" "ServiceIsLuncher"
09.06.2019 04:55 6008: CreateProcessAsUser(SG_WinService) 成功。新进程 ID:7536
因此,它不是在 Session 1 上运行子进程,而是作为“SYSTEM”运行。
解决方案
推荐阅读
- mongodb - Mongodb - 索引使用和应用
- python - 用于测试 Postgres 连接并获取响应代码的 Python 代码
- android - 颤振错误:没有为“对象”类型定义方法“数据”
- cypress - 当匹配字符串具有正斜杠时,应包含 cypress 中的失败
- kotlin - IntelliJ 不检测某些 Kotlin 类的源代码
- python - Python:模拟具有副作用的实例变量
- ubuntu - 构建 mediapipe 的手部跟踪示例时出错
- docker - 在 GCP VM 实例中构建和运行 docker 容器的问题
- vue.js - 如何从部署在 heroku 上的机器学习模型中获取数据?
- reactjs - 单页应用程序是否需要应用程序服务器?