I am creating a windows service that will print pdf in time interval.
 
I have done some research on this and found a class
- public class ProcessStarter : IDisposable  
- {  
- #region Import Section  
- private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;  
- private static uint STANDARD_RIGHTS_READ = 0x00020000;  
- private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;  
- private static uint TOKEN_DUPLICATE = 0x0002;  
- private static uint TOKEN_IMPERSONATE = 0x0004;  
- private static uint TOKEN_QUERY = 0x0008;  
- private static uint TOKEN_QUERY_SOURCE = 0x0010;  
- private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;  
- private static uint TOKEN_ADJUST_GROUPS = 0x0040;  
- private static uint TOKEN_ADJUST_DEFAULT = 0x0080;  
- private static uint TOKEN_ADJUST_SESSIONID = 0x0100;  
- private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);  
- private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);  
- private const uint NORMAL_PRIORITY_CLASS = 0x0020;  
- private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;  
- private const uint MAX_PATH = 260;  
- private const uint CREATE_NO_WINDOW = 0x08000000;  
- private const uint INFINITE = 0xFFFFFFFF;  
- [StructLayout(LayoutKind.Sequential)]  
- public struct SECURITY_ATTRIBUTES  
- {  
- public int nLength;  
- public IntPtr lpSecurityDescriptor;  
- public int bInheritHandle;  
- }  
- public enum SECURITY_IMPERSONATION_LEVEL  
- {  
- SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation  
- }  
- public enum TOKEN_TYPE  
- {  
- TokenPrimary = 1, TokenImpersonation  
- }  
- public enum WTS_CONNECTSTATE_CLASS  
- {  
- WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit  
- }  
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]  
- public struct STARTUPINFO  
- {  
- public Int32 cb;  
- public string lpReserved;  
- public string lpDesktop;  
- public string lpTitle;  
- public Int32 dwX;  
- public Int32 dwY;  
- public Int32 dwXSize;  
- public Int32 dwYSize;  
- public Int32 dwXCountChars;  
- public Int32 dwYCountChars;  
- public Int32 dwFillAttribute;  
- public Int32 dwFlags;  
- public Int16 wShowWindow;  
- public Int16 cbReserved2;  
- public IntPtr lpReserved2;  
- public IntPtr hStdInput;  
- public IntPtr hStdOutput;  
- public IntPtr hStdError;  
- }  
- [StructLayout(LayoutKind.Sequential)]  
- internal struct PROCESS_INFORMATION  
- {  
- public IntPtr hProcess;  
- public IntPtr hThread;  
- public int dwProcessId;  
- public int dwThreadId;  
- }  
- [StructLayout(LayoutKind.Sequential)] private struct WTS_SESSION_INFO  
- {  
- public Int32 SessionID;  
- [MarshalAs(UnmanagedType.LPStr)]  
- public String pWinStationName;  
- public WTS_CONNECTSTATE_CLASS State;  
- }  
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern uint WTSGetActiveConsoleSessionId();  
- [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle);  
- [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- public extern static bool DuplicateTokenEx(IntPtr existingToken, uint desiredAccess, IntPtr tokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr newToken);  
- [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern bool CreateProcessAsUser(IntPtr token, string applicationName, string commandLine, ref SECURITY_ATTRIBUTES processAttributes, ref SECURITY_ATTRIBUTES threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern bool CloseHandle(IntPtr handle);  
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern int GetLastError();  
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern int WaitForSingleObject(IntPtr token, uint timeInterval); [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern int WTSEnumerateSessions(System.IntPtr hServer, int Reserved, int Version, ref System.IntPtr ppSessionInfo, ref int pCount);  
- [DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]  
- static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);  
- [DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]  
- public static extern void WTSFreeMemory(IntPtr memory);  
- [DllImport("userenv.dll", SetLastError = true)]  
- [return: MarshalAs(UnmanagedType.Bool)]  
- static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);  
- #endregion  
-   
-   
-   
- public ProcessStarter() { }  
-   
-   
-   
-   
-   
- public ProcessStarter(string processName, string fullExeName)  
- {  
- processName_ = processName; processPath_ = fullExeName; }  
-   
-   
-   
-   
-   
-   
- public ProcessStarter(string processName, string fullExeName, string arguments)  
- {  
- processName_ = processName; processPath_ = fullExeName; arguments_ = arguments; }  
-   
-   
-   
-   
- 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; }  
-   
-   
-   
- public void Run()  
- {  
- IntPtr primaryToken = GetCurrentUserToken();  
- if (primaryToken == IntPtr.Zero)  
- {  
- return;  
- }  
- STARTUPINFO StartupInfo = new STARTUPINFO();  
- processInfo_ = new PROCESS_INFORMATION();  
- StartupInfo.cb = Marshal.SizeOf(StartupInfo);  
- SECURITY_ATTRIBUTES Security1 = new SECURITY_ATTRIBUTES();  
- SECURITY_ATTRIBUTES Security2 = new SECURITY_ATTRIBUTES();  
- string command = "\"" + processPath_ + "\""; if ((arguments_ != null) && (arguments_.Length != 0))  
- {  
- command += " " + arguments_;  
- }  
- IntPtr lpEnvironment = IntPtr.Zero;  
- bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);  
- if (resultEnv != true)  
- {  
- int nError = GetLastError();  
- }  
- CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);  
- DestroyEnvironmentBlock(lpEnvironment);  
- CloseHandle(primaryToken);  
- }  
-   
-   
-   
- public void Stop()  
- {  
- Process[] processes = Process.GetProcesses();  
- foreach (Process current in processes)  
- {  
- if (current.ProcessName == processName_)  
- {  
- current.Kill();  
- }  
- }  
- }  
-   
-   
-   
-   
- public int WaitForExit()  
- {  
- WaitForSingleObject(processInfo_.hProcess, INFINITE);  
- int errorcode = GetLastError();  
- return errorcode;  
- }  
- #region IDisposable Members  
-   
-   
-   
- public void Dispose()  
- {  
- }  
- #endregion  
- private string processPath_ = string.Empty;  
- private string processName_ = string.Empty;  
- private string arguments_ = string.Empty;  
- private PROCESS_INFORMATION processInfo_;  
-   
-   
-   
- <value>The process path.</value>  
- public string ProcessPath  
- {  
- get  
- {  
- return processPath_;  
- }  
- set  
- {  
- processPath_ = value;  
- }  
- }  
-   
-   
-   
-   
- public string ProcessName  
- {  
- get{ return processName_; }  
- set { processName_ = value; }  
- }  
-   
-   
-   
-   
- public string Arguments  
- {  
- get { return arguments_; }  
- set { arguments_ = value; }  
- }  
- }  
and calling this class like this
- string pdfPrinterLocation = @"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe";  
- string pdfArguments = "/t /o C:\Windows\TEMP\761372.pdf";  
- ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments);  
- processStarter.Run();  
- processStarter.WaitForExit();  
- processStarter.Stop();  
This code is working fine and doing great job.
 
But This is working only when some user logged in in system. Means when I login then it's working. But when I logoff my user then it's not working. When I lock my system then it's working but when logoff and switch user then it's not working.
 
I have checked service logs for checking where the issue is.
 
So I found this statement in Run method
- IntPtr primaryToken = GetCurrentUserToken();  
- if (primaryToken == IntPtr.Zero) { return; }  
When user logged in then primaryToken is good but when signout or switch user then it is IntPtr.Zero.
 
Then I have checked GetCurrentUserToken method. and I found that this method is creating token for current logged in user token. that's why this is not working after logoff.
 
But I want printing after logoff.
 
In this Run method CreateProcessAsUser is used for creating print process.
 
But I don't know how can I create process without user. There is a method CreateProcess. Perhaps it will create process without user token I don't know much about it. Found this here.
 
But it is also not working. Even it is not working while user logged in.
 
I want to print pdf files in some interval using windows services. And It should be keep working after user logoff or switch user or lock.
 
Anyone know how can I achieve this?
 
Thanks