我正在尝试使服务在Windows中打开的会话中创建一个进程。 我有这个代码:
sessionId =WTSGetActiveConsoleSessionId();
if (WTSQueryUserToken(sessionId,&dummy)) {
if (!DuplicateTokenEx(dummy, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &token)) {
CloseHandle(dummy);
return false;
}
CloseHandle(dummy);
// Create process for user with desktop
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, "before create!!!!\n");
fclose(myfile);
if (!CreateProcessAsUser(token, NULL,NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { // The "new console" is necessary. Otherwise the process can hang our main process
CloseHandle(token);
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, " create failed!\n");
fclose(myfile);
return false;
}
CloseHandle(token);
}
else {
myfile = fopen("c:\\temp\\test123.txt", "a");
fprintf(myfile, "Dummy fail\n");
fprintf(myfile, "last error is %d \n", GetLastError());
fclose(myfile);
}
//int ret = CreateProcess(FILE_EXEC, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
如果我在服务的安装中使用最后一个(注释的)一切正常,因为它在安装服务时运行,因此它发生在用户会话内但是当我希望服务这样做时它会失败,sessionId没问题,失败从:
开始 if (WTSQueryUserToken(sessionId,&dummy)) {
我知道WTSQueryUserToken是一个应该从服务运行的函数,sessionid是1(它是来自cmd检查的实数)并且假设在它之后保存用户令牌但由于某种原因它失败了。 ......有什么想法吗?
答案 0 :(得分:11)
我在自己的服务中使用类似于你的代码,它工作正常。有些事情需要考虑到你所展示的代码没有做到:
调用WTSQueryUserToken()
时,您必须确保您的服务进程已启用SE_TCB_NAME
权限。请使用AdjustTokenPrivileges()
。
WTSGetActiveConsoleSessionId()
返回的会话ID可能不是是您运行生成进程所需的正确会话!它返回附加到本地计算机的物理控制台(屏幕/键盘/鼠标)的会话ID(如果有)。该会话可能正在显示安全的WinLogon桌面,这意味着没有用户实际登录到物理计算机,因此对该会话ID调用WTSQueryUserToken()
将失败,并显示ERROR_NO_TOKEN
错误。例如,用户可以通过远程桌面连接登录,在这种情况下,该连接将在与控制台不同的会话中运行。如果您希望生成的进程在用户登录的会话中运行,则需要使用WTSEnumerateSessions()
来查找处于WTSActive
状态的会话。即使这样,WTSQueryUserToken()
也可能无法返回令牌,具体取决于用户的登录方式,因此您需要在找到的每个活动会话上调用WTSQueryUserToken()
,直到找到成功为您提供令牌的会话为止。
致电DuplicateTokenEx()
时,请使用SecurityIdentification
代替SecurityDelegation
。
调用CreateProcessAsUser()
时,您可以先调用CreateEnvironmentBlock()
创建适合该特定用户的环境,然后将该指针传递给CreateProcessAsUser()
。否则,生成的进程将使用您的服务环境。此步骤是可选的,具体取决于衍生应用程序的特定需求。
答案 1 :(得分:1)
代码正确无误。...确保包含 “ wtsapi.h” 头文件和此“ #pragma注释( lib,“ WtsApi32.lib”) “,因为加载dll很重要,否则您将获得链接错误(错误LNK2019:无法解析的外部符号 ) 。 并且也不需要复制令牌...并且不需要特权设置,因为从用户帐户中的服务调用该过程时,默认会话ID为0,即。 SYSTEM帐户..具有用户可以拥有的所有特权...
从服务调用流程时,我的代码可以正常工作:
LPCWSTR path=L"C:\\Windows\\System32\\notepad.exe";//change the path accordingly
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD nsid=1;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof( si );
HANDLE htoken;
DWORD sessionId;
sessionId =WTSGetActiveConsoleSessionId();
WTSQueryUserToken(sessionId,&htoken);
si.wShowWindow=TRUE;
if (CreateProcessAsUser(htoken, path, NULL, NULL,
NULL, FALSE, 0, NULL, NULL, &si, &pi ))
{
/* Process has been created; work with the process and wait for it to
terminate. */
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(htoken);
有关服务的清晰概念,请参考here。下载源代码,代码是完美的。 只需将这段代码插入'ServiceWorkerThread'函数中即可。该段代码用于从您的服务中打开.exe文件(例如,文件资源管理器,记事本等)。服务(来自Windows应用程序)。