我正在LocalSystem帐户下构建一个Windows服务,它将在某个时间间隔内打印pdf。
为此,我正在使用LogonUser
创建用户令牌
IntPtr currentToken = IntPtr.Zero;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_PROVIDER_DEFAULT = 0;
bool loggedOn = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref currentToken);
并在CreateProcessAsUser
中通过此currentToken
CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);
但它没有做它的工作
如果我使用以下代码获取当前用户令牌。
public static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 current = (int)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
{
return IntPtr.Zero;
}
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
{
return IntPtr.Zero;
}
return primaryToken;
}
然后CreateProcessAsUser works fine. But I need to create token by
LogonUserbecause after user logoff
GetCurrentUserToken' 方法不返回用户令牌,我在注销后也想要用户令牌。
更新我在像这样调用CreateProcessAsUser
后正在检查最后一个错误
uint exitCode;
if (!GetExitCodeProcess(processInfo_.hProcess, out exitCode))
{
int lastError = Marshal.GetLastWin32Error();
Logger.LogService(" GetExitCodeProcess Error " + lastError);
}
但是 GetExitCodeProcess 返回 true。我没有发现任何错误
首先
与上次发布此问题(从 Windows 服务打印 PDF 并在注销后继续工作)一样,您需要了解并找出哪个特定的 API 调用失败,然后调用GetLastError
这将为您提供有关调用失败原因的更多信息
GetLastError:检索调用线程的最后一个错误代码值
其次
可能是GetCurrentUserToken
中的一个调用,例如WTSQueryUserToken
可能只是存在权限问题或其他可以修复的问题(尽管我对此表示怀疑)
阅读文档以了解WTSQueryUserToken
它似乎陈述了以下内容
WTSQueryUserToken:获取由 指定的登录用户的主访问令牌 会话 ID。
此外,WTSQueryUserToken
可能会回归
ERROR_NO_TOKEN
1008
ERROR_NO_TOKEN:令牌查询适用于没有用户登录的会话。这 例如,当会话处于空闲状态或 会话 ID 为零。
第三
在我看来,我认为这种方法永远不会适用于您的情况,实际上不建议从服务打印。
请看打击类似的问题
从 Windows 服务打印
从 .NET 服务打印
最后
我唯一能想到的就是在可以访问打印机的用户帐户下运行您的服务,或者通过调用LogonUser
、LoadUserProfile
和ImpersonateLoggedOnUser
然后通过 Gdi+ 打印来模拟用户(这将有自己的问题)
请注意
虽然可以通过获取 打印机的设备上下文句柄,然后将该句柄传递给 GDI+ 图形构造函数,不建议这样做。 The GDI+ 不支持在 Windows 中使用函数和类 服务。 尝试从 Windows 使用这些函数和类 服务可能会产生意外问题,例如服务减少 性能和运行时异常或错误:
简而言之,您的解决方案和您当前拥有的源代码不太可能起作用,在我看来,您很可能会在这个问题上花费大量时间尝试做一些服务实际上不是设计要做的事情(这也超出了当前问题的范围)。
我真的认为你需要认真重新考虑你想做什么以及为什么,并祝你好运
有关 GDI 打印和从服务打印的其他信息
如何安装应该可以从Windows服务访问的打印机?
从 Windows 服务打印
公然从链接页面中窃取。
通常,调用 CreateProcessAsUser 函数的进程必须具有SE_INCREASE_QUOTA_NAME。
本地系统帐户具有以下权限: ... SE_INCREASE_QUOTA_NAME(禁用) 所以我很惊讶CreateProcessAsUser曾经在LocalSystem上下文中工作。
创建进程为用户不会将指定用户的配置文件加载到HKEY_USERS注册表项中。因此,若要访问HKEY_CURRENT_USER注册表项中的信息,必须在调用 CreateProcessAsUser 之前,使用 LoadUserProfile 函数将用户的配置文件信息加载到HKEY_USERS中。请务必在新进程退出后调用卸载用户配置文件。
CreateProcessAsUser 似乎比 CreateProcessWithLogonW 更挑剔。我会尝试使用LOGON_WITH_PROFILE参数创建进程与登录。
您在代码中的令牌上调用 DuplicateTokenEx,这似乎有效。 您可能想尝试使用 LogonUser 令牌执行此操作。
您没有提供有关打印过程的任何信息,但从您提供的信息来看,我想它一定需要在交互式会话下运行。您的第一种方法不起作用,因为LogonUser
证明的令牌与调用进程具有相同的会话 ID,即 0。注销后您的第二种方法不起作用是因为WTSQueryUserToken
会像预期的那样失败。因此,我建议您尝试winlogon解决方案,该解决方案可以概括为找出交互式会话的winlogon过程,复制其令牌并使用令牌创建打印过程。这个阿克特克有所有的细节。