I am building a windows services under LocalSystem account that will print pdf in some time interval.
To do this I am creating user token by using 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(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
LogonUser because after user logoff
GetCurrentUserToken` method is not returning user token and I want user token also after logoff.
Update I am checking last error after calling CreateProcessAsUser
like this
uint exitCode;
if (!GetExitCodeProcess(processInfo_.hProcess, out exitCode))
{
int lastError = Marshal.GetLastWin32Error();
Logger.LogService(" GetExitCodeProcess Error " + lastError);
}
But GetExitCodeProcess returns 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
GetLastError : Retrieves the calling thread's last-error code value
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)
Reading the documentation for WTSQueryUserToken
it seems to state the following
WTSQueryUserToken : Obtains the primary access token of the logged-on user specified by the session ID.
Further more, WTSQueryUserToken
is probably returning
ERROR_NO_TOKEN
1008
ERROR_NO_TOKEN : The token query is for a session in which no user is logged-on . This occurs, for example, when the session is in the idle state or SessionId is zero.
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
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)
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. The GDI+ functions and classes are not supported for use within a Windows service. 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:
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
How to install printer which should be accessible from windows service?
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. 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. 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.
The LocalSystem account has the following privileges: ... SE_INCREASE_QUOTA_NAME (disabled) So I am surprised that CreateProcessAsUser ever works in LocalSystem context.
CreateProcessAsUser does not load the specified user's profile into the HKEY_USERS registry key. 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. Be sure to call UnloadUserProfile after the new process exits.
CreateProcessAsUser seems a bit more picky than CreateProcessWithLogonW. I would try CreateProcessWithLogonW with the LOGON_WITH_PROFILE parameter.
You call DuplicateTokenEx on the token in the code that seems to work. You might want to try that with the LogonUser token.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.