简体   繁体   English

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

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

I am building a windows services under LocalSystem account that will print pdf in some time interval. 我正在LocalSystem帐户下构建一个Windows服务,它将在某个时间间隔内打印pdf。

To do this I am creating user token by using LogonUser 为此,我使用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);

and passing this currentToken in CreateProcessAsUser 并在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_);

But it's not doing it's job 但它没有做到这一点

If I get the current user token by using following code. 如果我使用以下代码获取当前用户令牌。

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;
}

Then CreateProcessAsUser works fine. But I need to create token by 然后CreateProcessAsUser works fine. But I need to create token by CreateProcessAsUser works fine. But I need to create token by LogonUser because after user logoff GetCurrentUserToken` method is not returning user token and I want user token also after logoff. 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`方法没有返回用户令牌,我也想在注销后获得用户令牌。

Update I am checking last error after calling CreateProcessAsUser like this 更新我在调用CreateProcessAsUser之后检查上一个错误

uint exitCode; 

if (!GetExitCodeProcess(processInfo_.hProcess, out exitCode)) 
{ 
    int lastError = Marshal.GetLastWin32Error(); 
    Logger.LogService(" GetExitCodeProcess Error " + lastError); 
} 

But GetExitCodeProcess returns true. 但GetExitCodeProcess返回true。 I didn't found any error 我没有发现任何错误

Firstly 首先

As with the last time you posted this question ( Print Pdf from windows service and keep working after logoff ), you need understand and workout what particular API call is failing and then call GetLastError which will give you more information about why the call is failing 与上次发布此问题( 从Windows服务打印Pdf并在注销后继续工作 )一样,您需要了解并解决特定API调用失败的问题,然后调用GetLastError ,它将为您提供有关调用失败原因的更多信息

GetLastError : Retrieves the calling thread's last-error code value GetLastError :检索调用线程的最后错误代码值

Secondly 其次

It could be the case that one of the calls in GetCurrentUserToken eg WTSQueryUserToken might just have a permission problem or something else that can be fixed (though i doubt it) 可能是GetCurrentUserToken中的一个调用,例如WTSQueryUserToken可能只有一个权限问题或其他可以修复的东西(尽管我对此表示怀疑)

Reading the documentation for WTSQueryUserToken it seems to state the following 阅读WTSQueryUserToken的文档,似乎说明如下

WTSQueryUserToken : Obtains the primary access token of the logged-on user specified by the session ID. WTSQueryUserToken :获取会话ID指定的登录用户的主要访问令牌。

Further more, WTSQueryUserToken is probably returning 此外, WTSQueryUserToken可能正在返回

ERROR_NO_TOKEN
1008

ERROR_NO_TOKEN : The token query is for a session in which no user is logged-on . ERROR_NO_TOKEN :令牌查询用于没有用户登录的会话。 This occurs, for example, when the session is in the idle state or SessionId is zero. 例如,当会话处于空闲状态或SessionId为零时,会发生这种情况。

Thirdly 第三

In my opinion, I don't think this approach is ever going to work in your situation and printing from a service is actually not recommended officially. 在我看来,我认为这种方法不会在您的情况下发挥作用,并且实际上不建议正式推出服务打印。

Please see the blow similar questions 请看看类似的问题

Printing from a Windows Service 从Windows服务打印

Printing from a .NET Service 从.NET服务打印

Lastly 最后

The only thing i can think of, is running you service under a User Account with access to the printers or impersonating a user by calling LogonUser , LoadUserProfile , and ImpersonateLoggedOnUser then printing via Gdi+ (which is going have its own problems) 我唯一能想到的是,在用户帐户下运行您的服务,可以访问打印机或通过调用LogonUserLoadUserProfileImpersonateLoggedOnUser用户,然后通过Gdi +打印(这有其自身的问题)

Please note 请注意

While it is possible to send GDI+ output to a printer by obtaining a device context handle for the printer and then passing that handle to a GDI+ Graphics constructor, this is not recommended. 虽然可以通过获取打印机的设备上下文句柄然后将该句柄传递给GDI + Graphics构造函数来将GDI +输出发送到打印机,但不建议这样做。 The GDI+ functions and classes are not supported for use within a Windows service. 在Windows服务中不支持GDI +函​​数和类。 Attempting to use these functions and classes from a Windows service may produce unexpected problems, such as diminished service performance and run-time exceptions or errors: 尝试从Windows服务使用这些函数和类可能会产生意外问题,例如服务性能下降和运行时异常或错误:


In short, you solution as it stands and the source code you currently have is unlikely to work, and in my opinion you are most likely going to spend a lot of time on this problem trying to do something services are really not designed to do (which is also out of the scope of the current question). 简而言之,您现在的解决方案和您目前拥有的源代码不太可行,而且在我看来,您很可能会花很多时间在这个问题上尝试做某些服务真的不是为了做(这也超出了当前问题的范围)。

I really think you need to seriously reconsider what you are trying to do and why and wish you the best of luck 我真的认为你需要认真重新考虑你想做什么以及为什么,祝你好运

Additional information on GDI Printing and printing from a service 有关GDI打印和从服务打印的其他信息

How to install printer which should be accessible from windows service? 如何安装应该可以从Windows服务访问的打印机?

Printing from a Windows Service 从Windows服务打印

You didn't give any information about your printing process, but from what you provided, I guess it must need to be run under interactive session. 您没有提供有关打印过程的任何信息,但是根据您提供的信息,我想它必须在交互式会话下运行。 Your first way didn't work because the token provede by LogonUser has same session id as the calling process which is 0. And your second way didn't work after log out is because WTSQueryUserToken will fail as it suppose to. 你的第一种方式不起作用,因为LogonUser的token provede与调用进程的会话ID相同,而且你的第二种方式在注销后无效,因为WTSQueryUserToken会因为它的假设而失败。 So I suggest you try winlogon solution which could be summarized as find out winlogon process of interactive session, duplicate its token and use the token to create you printing process. 所以我建议你尝试winlogon解决方案,可以概括为查找交互式会话的winlogon进程,复制其令牌并使用令牌创建打印过程。 This acticle has all the details. 这篇文章包含了所有细节。

Blatantly stolen from the linked pages. 从链接页面中大肆窃取。

Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME. 通常,调用CreateProcessAsUser函数的进程必须具有SE_INCREASE_QUOTA_NAME。

The LocalSystem account has the following privileges: ... SE_INCREASE_QUOTA_NAME (disabled) So I am surprised that CreateProcessAsUser ever works in LocalSystem context. LocalSystem帐户具有以下权限:... SE_INCREASE_QUOTA_NAME(已禁用)因此,我很惊讶CreateProcessAsUser在LocalSystem上下文中工作。

CreateProcessAsUser does not load the specified user's profile into the HKEY_USERS registry key. CreateProcessAsUser不会将指定用户的配置文件加载到HKEY_USERS注册表项中。 Therefore, to access the information in the HKEY_CURRENT_USER registry key, you must load the user's profile information into HKEY_USERS with the LoadUserProfile function before calling CreateProcessAsUser. 因此,要访问HKEY_CURRENT_USER注册表项中的信息,必须在调用CreateProcessAsUser之前使用LoadUserProfile函数将用户的配置文件信息加载到HKEY_USERS中。 Be sure to call UnloadUserProfile after the new process exits. 在新进程退出后,请务必调用UnloadUserProfile。

CreateProcessAsUser seems a bit more picky than CreateProcessWithLogonW. CreateProcessAsUser似乎比CreateProcessWithLogonW更挑剔。 I would try CreateProcessWithLogonW with the LOGON_WITH_PROFILE parameter. 我会尝试使用LOGON_WITH_PROFILE参数创建CreateProcessWithLogonW

You call DuplicateTokenEx on the token in the code that seems to work. 您可以在代码中的令牌上调用DuplicateTokenEx。 You might want to try that with the LogonUser token. 您可能希望使用LogonUser令牌尝试此操作。

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

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