Manoj Kumawat

Manoj Kumawat

  • 1.4k
  • 244
  • 2.1k

Print Pdf from windows service and keep working after logoff

Jan 25 2018 4:29 AM
I am creating a windows service that will print pdf in time interval.
 
I have done some research on this and found a class
  1. public class ProcessStarter : IDisposable  
  2. {  
  3. #region Import Section  
  4. private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;  
  5. private static uint STANDARD_RIGHTS_READ = 0x00020000;  
  6. private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;  
  7. private static uint TOKEN_DUPLICATE = 0x0002;  
  8. private static uint TOKEN_IMPERSONATE = 0x0004;  
  9. private static uint TOKEN_QUERY = 0x0008;  
  10. private static uint TOKEN_QUERY_SOURCE = 0x0010;  
  11. private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;  
  12. private static uint TOKEN_ADJUST_GROUPS = 0x0040;  
  13. private static uint TOKEN_ADJUST_DEFAULT = 0x0080;  
  14. private static uint TOKEN_ADJUST_SESSIONID = 0x0100;  
  15. private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);  
  16. 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);  
  17. private const uint NORMAL_PRIORITY_CLASS = 0x0020;  
  18. private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;  
  19. private const uint MAX_PATH = 260;  
  20. private const uint CREATE_NO_WINDOW = 0x08000000;  
  21. private const uint INFINITE = 0xFFFFFFFF;  
  22. [StructLayout(LayoutKind.Sequential)]  
  23. public struct SECURITY_ATTRIBUTES  
  24. {  
  25. public int nLength;  
  26. public IntPtr lpSecurityDescriptor;  
  27. public int bInheritHandle;  
  28. }  
  29. public enum SECURITY_IMPERSONATION_LEVEL  
  30. {  
  31. SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation  
  32. }  
  33. public enum TOKEN_TYPE  
  34. {  
  35. TokenPrimary = 1, TokenImpersonation  
  36. }  
  37. public enum WTS_CONNECTSTATE_CLASS  
  38. {  
  39. WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit  
  40. }  
  41. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]  
  42. public struct STARTUPINFO  
  43. {  
  44. public Int32 cb;  
  45. public string lpReserved;  
  46. public string lpDesktop;  
  47. public string lpTitle;  
  48. public Int32 dwX;  
  49. public Int32 dwY;  
  50. public Int32 dwXSize;  
  51. public Int32 dwYSize;  
  52. public Int32 dwXCountChars;  
  53. public Int32 dwYCountChars;  
  54. public Int32 dwFillAttribute;  
  55. public Int32 dwFlags;  
  56. public Int16 wShowWindow;  
  57. public Int16 cbReserved2;  
  58. public IntPtr lpReserved2;  
  59. public IntPtr hStdInput;  
  60. public IntPtr hStdOutput;  
  61. public IntPtr hStdError;  
  62. }  
  63. [StructLayout(LayoutKind.Sequential)]  
  64. internal struct PROCESS_INFORMATION  
  65. {  
  66. public IntPtr hProcess;  
  67. public IntPtr hThread;  
  68. public int dwProcessId;  
  69. public int dwThreadId;  
  70. }  
  71. [StructLayout(LayoutKind.Sequential)] private struct WTS_SESSION_INFO  
  72. {  
  73. public Int32 SessionID;  
  74. [MarshalAs(UnmanagedType.LPStr)]  
  75. public String pWinStationName;  
  76. public WTS_CONNECTSTATE_CLASS State;  
  77. }  
  78. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  79. static extern uint WTSGetActiveConsoleSessionId();  
  80. [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  81. static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle);  
  82. [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  83. public extern static bool DuplicateTokenEx(IntPtr existingToken, uint desiredAccess, IntPtr tokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr newToken);  
  84. [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  85. 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)]  
  86. static extern bool CloseHandle(IntPtr handle);  
  87. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  88. static extern int GetLastError();  
  89. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  90. static extern int WaitForSingleObject(IntPtr token, uint timeInterval); [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  91. static extern int WTSEnumerateSessions(System.IntPtr hServer, int Reserved, int Version, ref System.IntPtr ppSessionInfo, ref int pCount);  
  92. [DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  93. static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);  
  94. [DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]  
  95. public static extern void WTSFreeMemory(IntPtr memory);  
  96. [DllImport("userenv.dll", SetLastError = true)]  
  97. [return: MarshalAs(UnmanagedType.Bool)]  
  98. static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);  
  99. #endregion  
  100. /// <summary>  
  101. /// Initializes a new instance of the <see cref="ProcessStarter"/> class.  
  102. /// </summary>  
  103. public ProcessStarter() { }  
  104. /// <summary>  
  105. /// Initializes a new instance of the <see cref="ProcessStarter"/> class.  
  106. /// </summary>  
  107. /// <param name="processName">Name of the process.</param>  
  108. /// <param name="fullExeName">Full name of the exe.</param>  
  109. public ProcessStarter(string processName, string fullExeName)  
  110. {  
  111. processName_ = processName; processPath_ = fullExeName; }  
  112. /// <summary>  
  113. /// Initializes a new instance of the <see cref="ProcessStarter"/> class.  
  114. /// </summary>  
  115. /// <param name="processName">Name of the process.</param>  
  116. /// <param name="fullExeName">Full name of the exe.</param>  
  117. /// <param name="arguments">The arguments.</param>  
  118. public ProcessStarter(string processName, string fullExeName, string arguments)  
  119. {  
  120. processName_ = processName; processPath_ = fullExeName; arguments_ = arguments; }  
  121. /// <summary>  
  122. /// Gets the current user token.  
  123. /// </summary>  
  124. /// <returns></returns>  
  125. public static IntPtr GetCurrentUserToken()  
  126. {  
  127. IntPtr currentToken = IntPtr.Zero;  
  128. IntPtr primaryToken = IntPtr.Zero;  
  129. IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;  
  130. int dwSessionId = 0;  
  131. IntPtr hUserToken = IntPtr.Zero;  
  132. IntPtr hTokenDup = IntPtr.Zero;  
  133. IntPtr pSessionInfo = IntPtr.Zero;  
  134. int dwCount = 0; WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);  
  135. Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));  
  136. Int32 current = (int)pSessionInfo;  
  137. for (int i = 0; i < dwCount; i++)  
  138. {  
  139. WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));  
  140. if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)  
  141. {  
  142. dwSessionId = si.SessionID;  
  143. break;  
  144. }  
  145. current += dataSize;  
  146. }  
  147. WTSFreeMemory(pSessionInfo);  
  148. bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);  
  149. if (bRet == false)  
  150. {  
  151. return IntPtr.Zero;  
  152. }  
  153. bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);  
  154. if (bRet == false)  
  155. {  
  156. return IntPtr.Zero;  
  157. }  
  158. return primaryToken; }  
  159. /// <summary>  
  160. /// Runs this instance.  
  161. /// </summary>  
  162. public void Run()  
  163. {  
  164. IntPtr primaryToken = GetCurrentUserToken();  
  165. if (primaryToken == IntPtr.Zero)  
  166. {  
  167. return;  
  168. }  
  169. STARTUPINFO StartupInfo = new STARTUPINFO();  
  170. processInfo_ = new PROCESS_INFORMATION();  
  171. StartupInfo.cb = Marshal.SizeOf(StartupInfo);  
  172. SECURITY_ATTRIBUTES Security1 = new SECURITY_ATTRIBUTES();  
  173. SECURITY_ATTRIBUTES Security2 = new SECURITY_ATTRIBUTES();  
  174. string command = "\"" + processPath_ + "\""if ((arguments_ != null) && (arguments_.Length != 0))  
  175. {  
  176. command += " " + arguments_;  
  177. }  
  178. IntPtr lpEnvironment = IntPtr.Zero;  
  179. bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);  
  180. if (resultEnv != true)  
  181. {  
  182. int nError = GetLastError();  
  183. }  
  184. CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, nullref StartupInfo, out processInfo_);  
  185. DestroyEnvironmentBlock(lpEnvironment);  
  186. CloseHandle(primaryToken);  
  187. }  
  188. /// <summary>  
  189. /// Stops this instance.  
  190. /// </summary>  
  191. public void Stop()  
  192. {  
  193. Process[] processes = Process.GetProcesses();  
  194. foreach (Process current in processes)  
  195. {  
  196. if (current.ProcessName == processName_)  
  197. {  
  198. current.Kill();  
  199. }  
  200. }  
  201. }  
  202. /// <summary>  
  203. /// Waits for exit.  
  204. /// </summary>  
  205. /// <returns></returns>  
  206. public int WaitForExit()  
  207. {  
  208. WaitForSingleObject(processInfo_.hProcess, INFINITE);  
  209. int errorcode = GetLastError();  
  210. return errorcode;  
  211. }  
  212. #region IDisposable Members  
  213. /// <summary>  
  214. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.  
  215. /// </summary>  
  216. public void Dispose()  
  217. {  
  218. }  
  219. #endregion  
  220. private string processPath_ = string.Empty;  
  221. private string processName_ = string.Empty;  
  222. private string arguments_ = string.Empty;  
  223. private PROCESS_INFORMATION processInfo_;  
  224. /// <summary>  
  225. /// Gets or sets the process path.  
  226. /// </summary> ///  
  227. <value>The process path.</value>  
  228. public string ProcessPath  
  229. {  
  230. get  
  231. {  
  232. return processPath_;  
  233. }  
  234. set  
  235. {  
  236. processPath_ = value;  
  237. }  
  238. }  
  239. /// <summary>  
  240. /// Gets or sets the name of the process.  
  241. /// </summary>  
  242. /// <value>The name of the process.</value>  
  243. public string ProcessName  
  244. {  
  245. getreturn processName_; }  
  246. set { processName_ = value; }  
  247. }  
  248. /// <summary>  
  249. /// Gets or sets the arguments.  
  250. /// </summary>  
  251. /// <value>The arguments.</value>  
  252. public string Arguments  
  253. {  
  254. get { return arguments_; }  
  255. set { arguments_ = value; }  
  256. }  
  257. }  
and calling this class like this
  1. string pdfPrinterLocation = @"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe";  
  2. string pdfArguments = "/t /o C:\Windows\TEMP\761372.pdf";  
  3. ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments);  
  4. processStarter.Run();  
  5. processStarter.WaitForExit();  
  6. 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
  1. IntPtr primaryToken = GetCurrentUserToken();  
  2. 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

Answers (1)