繁体   English   中英

从Windows服务打印时,CreateProcessAsUser不使用LogonUser令牌

[英]CreateProcessAsUser not working with LogonUser token when printing from a Windows Service

我正在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 CreateProcessAsUser works fine. But I need to create token by LogonUser CreateProcessAsUser works fine. But I need to create token by because 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 :令牌查询用于没有用户登录的会话。 例如,当会话处于空闲状态或SessionId为零时,会发生这种情况。

第三

在我看来,我认为这种方法不会在您的情况下发挥作用,并且实际上不建议正式推出服务打印。

请看看类似的问题

从Windows服务打印

从.NET服务打印

最后

我唯一能想到的是,在用户帐户下运行您的服务,可以访问打印机或通过调用LogonUserLoadUserProfileImpersonateLoggedOnUser用户,然后通过Gdi +打印(这有其自身的问题)

请注意

虽然可以通过获取打印机的设备上下文句柄然后将该句柄传递给GDI + Graphics构造函数来将GDI +输出发送到打印机,但不建议这样做。 在Windows服务中不支持GDI +函​​数和类。 尝试从Windows服务使用这些函数和类可能会产生意外问题,例如服务性能下降和运行时异常或错误:


简而言之,您现在的解决方案和您目前拥有的源代码不太可行,而且在我看来,您很可能会花很多时间在这个问题上尝试做某些服务真的不是为了做(这也超出了当前问题的范围)。

我真的认为你需要认真重新考虑你想做什么以及为什么,祝你好运

有关GDI打印和从服务打印的其他信息

如何安装应该可以从Windows服务访问的打印机?

从Windows服务打印

您没有提供有关打印过程的任何信息,但是根据您提供的信息,我想它必须在交互式会话下运行。 你的第一种方式不起作用,因为LogonUser的token provede与调用进程的会话ID相同,而且你的第二种方式在注销后无效,因为WTSQueryUserToken会因为它的假设而失败。 所以我建议你尝试winlogon解决方案,可以概括为查找交互式会话的winlogon进程,复制其令牌并使用令牌创建打印过程。 这篇文章包含了所有细节。

从链接页面中大肆窃取。

通常,调用CreateProcessAsUser函数的进程必须具有SE_INCREASE_QUOTA_NAME。

LocalSystem帐户具有以下权限:... SE_INCREASE_QUOTA_NAME(已禁用)因此,我很惊讶CreateProcessAsUser在LocalSystem上下文中工作。

CreateProcessAsUser不会将指定用户的配置文件加载到HKEY_USERS注册表项中。 因此,要访问HKEY_CURRENT_USER注册表项中的信息,必须在调用CreateProcessAsUser之前使用LoadUserProfile函数将用户的配置文件信息加载到HKEY_USERS中。 在新进程退出后,请务必调用UnloadUserProfile。

CreateProcessAsUser似乎比CreateProcessWithLogonW更挑剔。 我会尝试使用LOGON_WITH_PROFILE参数创建CreateProcessWithLogonW

您可以在代码中的令牌上调用DuplicateTokenEx。 您可能希望使用LogonUser令牌尝试此操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM