How to print PDF document from Windows Service - c#

I have windows service that must periodically print PDF document from server. My function is
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments = string.Format("/p /h\"{0}\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
ProcessStartInfo newProcess = new ProcessStartInfo(pdfPrinterLocation, pdfArguments);
newProcess.CreateNoWindow = true;
newProcess.RedirectStandardOutput = true;
newProcess.UseShellExecute = false;
newProcess.RedirectStandardError = true;
Process pdfProcess = new Process();
pdfProcess.StartInfo = newProcess;
pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pdfProcess.Start();
pdfProcess.WaitForExit();
}
When I implement this in Windows Application it works, but when I implement in Windows service it does not work.
Can you help me?

I solved problem with Session 0. I use this 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
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
public ProcessStarter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
public ProcessStarter(string processName, string fullExeName)
{
processName_ = processName;
processPath_ = fullExeName;
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
/// <param name="arguments">The arguments.</param>
public ProcessStarter(string processName, string fullExeName, string arguments)
{
processName_ = processName;
processPath_ = fullExeName;
arguments_ = arguments;
}
/// <summary>
/// Gets the current user token.
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Runs this instance.
/// </summary>
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);
}
/// <summary>
/// Stops this instance.
/// </summary>
public void Stop()
{
Process[] processes = Process.GetProcesses();
foreach (Process current in processes)
{
if (current.ProcessName == processName_)
{
current.Kill();
}
}
}
/// <summary>
/// Waits for exit.
/// </summary>
/// <returns></returns>
public int WaitForExit()
{
WaitForSingleObject(processInfo_.hProcess, INFINITE);
int errorcode = GetLastError();
return errorcode;
}
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
}
#endregion
private string processPath_ = string.Empty;
private string processName_ = string.Empty;
private string arguments_ = string.Empty;
private PROCESS_INFORMATION processInfo_;
/// <summary>
/// Gets or sets the process path.
/// </summary>
/// <value>The process path.</value>
public string ProcessPath
{
get
{
return processPath_;
}
set
{
processPath_ = value;
}
}
/// <summary>
/// Gets or sets the name of the process.
/// </summary>
/// <value>The name of the process.</value>
public string ProcessName
{
get
{
return processName_;
}
set
{
processName_ = value;
}
}
/// <summary>
/// Gets or sets the arguments.
/// </summary>
/// <value>The arguments.</value>
public string Arguments
{
get
{
return arguments_;
}
set
{
arguments_ = value;
}
}
}
Now, my function look like:
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments =string.Format("/t /o {0} \"Printer name\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
try
{
ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments);
processStarter.Run();
processStarter.WaitForExit();
processStarter.Stop();
}
finally
{
File.Delete(tempFile);
}
}
Also, ServiceProcessInstaller must have `Account set to "LocalSystem". When I created service I set to "Local service" with this user it does not work. I did not try with "Network service" or "User".

I solved this problem with the registry edits found in this article: https://support.microsoft.com/en-us/kb/184291.
Printing from Windows services (and also IIS) uses the SYSTEM account as I understand it. This account does not have access to the printer but these registry edits gives that SYSTEM account printing privileges.
Here is a summary:
export 3 keys ("folders within regedit.exe") to .reg files:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Devices
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\PrinterPorts
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Windows
edit your 3 newly created reg files and change "HKEY_CURRENT_USER" to "HKEY_USERS\.DEFAULT"
run your 3 reg files by double clicking them in explorer (this adds them to the registry)
That's all it takes. This helped me to print pdf files to a printer from PHP in IIS 7. My php script would work just fine so long as the server happened to have the Administrator logged in when the script was run. Now no one needs to be logged in and the script still works :)
I hope this saves someone as much searching and tinkering as I put in to find it.

You didn't mention the OS, but I suspect you are running into the Session 0 isolation feature that was added in Windows Vista, Server 2008 and subsequent OS releases
Application classes affected by this feature include:
Services that create UI.
A service that tries to use window-message functions such as SendMessage and PostMessage to communicate with an application.
Applications creating globally named objects.

Related

Windows printer driver

we currently have bought a program for calculation etc, but which us don't allow duplex print. But our real Printer does support it.
I wanted to ask if it is possible to set a "printer driver" between the application and the real printer, which i can send the document too. In this driver i say that it should print in duplex.
im currently programming only with C# and .NET and i had never been further with c++ or c
can you give me some hints to get started with that?
The duplex class is from a microsoft documentation,forgot the actual link
Attached for you to use,
using System.Runtime.InteropServices;
using System;
namespace STACK.OVERFLOW
{
public class Duplexprint
{
#region Win32 API Declaration
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 GetLastError();
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)]
string pDeviceNameg, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
[DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel, IntPtr pPrinter, Int32 dwBuf, ref Int32 dwNeeded);
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, ref PRINTER_DEFAULTS pDefault);
[DllImport("winspool.Drv", EntryPoint = "SetPrinterA", ExactSpelling = true, SetLastError = true)]
public static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command);
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public IntPtr pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_INFO_9
{
public IntPtr pDevMode;
// Pointer to SECURITY_DESCRIPTOR
public int pSecurityDescriptor;
}
public const short CCDEVICENAME = 32;
public const short CCFORMNAME = 32;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
public const Int64 DM_DUPLEX = 0x1000L;
public const Int64 DM_ORIENTATION = 0x1L;
public const Int64 DM_SCALE = 0x10L;
public const Int64 DMORIENT_PORTRAIT = 0x1L;
public const Int64 DMORIENT_LANDSCAPE = 0x2L;
public const Int32 DM_MODIFY = 8;
public const Int32 DM_COPY = 2;
public const Int32 DM_IN_BUFFER = 8;
public const Int32 DM_OUT_BUFFER = 2;
public const Int32 PRINTER_ACCESS_ADMINISTER = 0x4;
public const Int32 PRINTER_ACCESS_USE = 0x8;
public const Int32 STANDARD_RIGHTS_REQUIRED = 0xf0000;
public const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
//added this
public const int CCHDEVICENAME = 32;
//added this
public const int CCHFORMNAME = 32;
#endregion
#region Public Methods
/// <summary>
/// Method Name : GetPrinterDuplex
/// Programmatically get the Duplex flag for the specified printer
/// driver's default properties.
/// </summary>
/// <param name="sPrinterName"> The name of the printer to be used. </param>
/// <param name="errorMessage"> this will contain error messsage if any. </param>
/// <returns>
/// nDuplexSetting - One of the following standard settings:
/// 0 = Error
/// 1 = None (Simplex)
/// 2 = Duplex on long edge (book)
/// 3 = Duplex on short edge (legal)
/// </returns>
/// <remarks>
/// </remarks>
public short GetPrinterDuplex(string sPrinterName, out string errorMessage)
{
errorMessage = string.Empty;
short functionReturnValue = 0;
IntPtr hPrinter = default(IntPtr);
PRINTER_DEFAULTS pd = default(PRINTER_DEFAULTS);
DEVMODE dm = new DEVMODE();
int nRet = 0;
pd.DesiredAccess = PRINTER_ACCESS_USE;
nRet = OpenPrinter(sPrinterName, out hPrinter, ref pd);
if ((nRet == 0) | (hPrinter.ToInt32() == 0))
{
if (GetLastError() == 5)
{
errorMessage = "Access denied -- See the article for more info.";
}
else
{
errorMessage = "Cannot open the printer specified " + "(make sure the printer name is correct).";
}
return functionReturnValue;
}
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, IntPtr.Zero, 0);
if ((nRet < 0))
{
errorMessage = "Cannot get the size of the DEVMODE structure.";
goto cleanup;
}
IntPtr iparg = Marshal.AllocCoTaskMem(nRet + 100);
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, iparg, IntPtr.Zero, DM_OUT_BUFFER);
if ((nRet < 0))
{
errorMessage = "Cannot get the DEVMODE structure.";
goto cleanup;
}
dm = (DEVMODE)Marshal.PtrToStructure(iparg, dm.GetType());
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
errorMessage = "You cannot modify the duplex flag for this printer " + "because it does not support duplex or the driver " + "does not support setting it from the Windows API.";
goto cleanup;
}
functionReturnValue = dm.dmDuplex;
cleanup:
if ((hPrinter.ToInt32() != 0))
ClosePrinter(hPrinter);
return functionReturnValue;
}
/// <summary>
/// Method Name : SetPrinterDuplex
/// Programmatically set the Duplex flag for the specified printer driver's default properties.
/// </summary>
/// <param name="sPrinterName"> sPrinterName - The name of the printer to be used. </param>
/// <param name="nDuplexSetting">
/// nDuplexSetting - One of the following standard settings:
/// 1 = None
/// 2 = Duplex on long edge (book)
/// 3 = Duplex on short edge (legal)
/// </param>
/// <param name="errorMessage"> this will contain error messsage if any. </param>
/// <returns>
/// Returns: True on success, False on error.
/// </returns>
/// <remarks>
///
/// </remarks>
public bool SetPrinterDuplex(string sPrinterName, int nDuplexSetting, out string errorMessage)
{
errorMessage = string.Empty;
bool functionReturnValue = false;
IntPtr hPrinter = default(IntPtr);
PRINTER_DEFAULTS pd = default(PRINTER_DEFAULTS);
PRINTER_INFO_9 pinfo = new PRINTER_INFO_9();
DEVMODE dm = new DEVMODE();
IntPtr ptrPrinterInfo = default(IntPtr);
int nBytesNeeded = 0;
int nRet = 0;
Int32 nJunk = default(Int32);
if ((nDuplexSetting < 1) | (nDuplexSetting > 3))
{
errorMessage = "Error: dwDuplexSetting is incorrect.";
return functionReturnValue;
}
pd.DesiredAccess = PRINTER_ACCESS_USE;
nRet = OpenPrinter(sPrinterName, out hPrinter, ref pd);
if ((nRet == 0) | (hPrinter.ToInt32() == 0))
{
if (GetLastError() == 5)
{
errorMessage = "Access denied -- See the article for more info.";
}
else
{
errorMessage = "Cannot open the printer specified " + "(make sure the printer name is correct).";
}
return functionReturnValue;
}
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, IntPtr.Zero, 0);
if ((nRet < 0))
{
errorMessage = "Cannot get the size of the DEVMODE structure.";
goto cleanup;
}
IntPtr iparg = Marshal.AllocCoTaskMem(nRet + 100);
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, iparg, IntPtr.Zero, DM_OUT_BUFFER);
if ((nRet < 0))
{
errorMessage = "Cannot get the DEVMODE structure.";
goto cleanup;
}
dm = (DEVMODE)Marshal.PtrToStructure(iparg, dm.GetType());
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
errorMessage = "You cannot modify the duplex flag for this printer " + "because it does not support duplex or the driver " + "does not support setting it from the Windows API.";
goto cleanup;
}
dm.dmDuplex = (short)nDuplexSetting;
Marshal.StructureToPtr(dm, iparg, true);
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pinfo.pDevMode, pinfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
if ((nRet < 0))
{
errorMessage = "Unable to set duplex setting to this printer.";
goto cleanup;
}
GetPrinter(hPrinter, 9, IntPtr.Zero, 0, ref nBytesNeeded);
if ((nBytesNeeded == 0))
{
errorMessage = "GetPrinter failed.";
goto cleanup;
}
ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded + 100);
nRet = GetPrinter(hPrinter, 9, ptrPrinterInfo, nBytesNeeded, ref nJunk) ? 1 : 0;
if ((nRet == 0))
{
errorMessage = "Unable to get shared printer settings.";
goto cleanup;
}
pinfo = (PRINTER_INFO_9)Marshal.PtrToStructure(ptrPrinterInfo, pinfo.GetType());
pinfo.pDevMode = iparg;
pinfo.pSecurityDescriptor = 0;
Marshal.StructureToPtr(pinfo, ptrPrinterInfo, true);
nRet = SetPrinter(hPrinter, 9, ptrPrinterInfo, 0) ? 1 : 0;
if ((nRet == 0))
{
errorMessage = "Unable to set shared printer settings.";
}
functionReturnValue = Convert.ToBoolean(nRet);
cleanup:
if ((hPrinter.ToInt32() != 0))
ClosePrinter(hPrinter);
return functionReturnValue;
}
#endregion
}
}
Example on the usage of the function
private string GetDefaultPrinter()
{
PrinterSettings settings = new PrinterSettings();
foreach (string printer in PrinterSettings.InstalledPrinters)
{
settings.PrinterName = printer;
if (settings.IsDefaultPrinter)
return printer;
}
return string.Empty;
}
public void printthis()
{
Duplexprint ds = new Duplexprint();
string printername = GetDefaultPrinter();
string errorMessage = string.Empty;
ds.SetPrinterDuplex(printername, 2, out errorMessage);
object Co = "1";
object Pa = "1";
object oT = true;
object oF = false;
if (Apptype == Applicationtypes.Word)
{
oDoc.PrintOut(ref oT, ref oF, ref obj, ref obj, ref obj,
ref obj, ref obj, ref Co, ref Pa, ref obj, ref oF, ref oT,
ref obj, ref obj, ref obj, ref obj, ref obj, ref obj);
}
else
{
xlWorkSheet.PrintOut(Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing);
}
ds.SetPrinterDuplex(printername, 1, out errorMessage);
}

WTSQueryUserToken returning FALSE

I'm making a interactive service on Windows 7, on visual studio 2015, that is able to initialize an application UI but the WTSQueryUserToken method is retuning false.
IntPtr hToken = IntPtr.Zero;
if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) //FALSE returned
I really don't have much experience with C# so I've searched online to get an answer and I found that "To call this function (WTSQueryUserToken) successfully, the calling application must be running within the context of the LocalSystem account and have the SE_TCB_NAME privilege", but I don't know how can I give the application SE_TCB_NAME privilege privilege on the code. Does anyone know how can I code this?
Thank you.
You have to do impersonation to launch the application under the user account.
Here is a sample of that, showing also how to get the SE_TCB_NAME privilege.
In this sample we first get the security token for the Current Process and then escalate the privileges to include the SE_TCB_NAME privilege.
Once we are done with that then we take the SessionId of the explorer process, and duplicate the security token associated with it, which we then pass to CreateProcessAsUser.
public class Impersonation
{
#region DLL Imports
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
internal const int TOKEN_ASSIGN_PRIMARY = 0x0001;
internal const int TOKEN_DUPLICATE = 0x0002;
internal const int TOKEN_IMPERSONATE = 0X00000004;
internal const int TOKEN_ADJUST_DEFAULT = 0x0080;
internal const int TOKEN_ADJUST_SESSIONID = 0x0100;
internal const int MAXIMUM_ALLOWED = 0x2000000;
internal const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
internal const int NORMAL_PRIORITY_CLASS = 0x20;
internal const int CREATE_NEW_CONSOLE = 0x00000010;
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
internal const string SE_TCB_NAME = "SeTcbPrivilege";
internal const string SE_RESTORE_NAME = "SeRestorePrivilege";
private static WindowsImpersonationContext impersonatedUser;
public static IntPtr hToken = IntPtr.Zero;
public static IntPtr dupeTokenHandle = IntPtr.Zero;
const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum ShowCommands : int
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
static extern bool DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel, Int32 dwTokenType,
ref IntPtr phNewToken);
[DllImport("userenv.dll", SetLastError = true)]
static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
#endregion
private static readonly ILog log = LogManager.GetLogger(typeof(Impersonation));
private static void WriteToLog(string message)
{
log.Debug(message);
}
/// <summary>
/// Duplicates the token information derived
/// from the logged in user's credentials. This
/// is required to run the application on the
/// logged in users desktop.
/// </summary>
/// <returns>Returns true if the application was successfully started in the user's desktop.</returns>
public static bool ExecuteAppAsLoggedOnUser(string AppName, string CmdLineArgs)
{
WriteToLog("In ExecuteAppAsLoggedOnUser for all users.");
IntPtr LoggedInUserToken = IntPtr.Zero;
IntPtr DuplicateToken = IntPtr.Zero;
IntPtr ShellProcessToken = IntPtr.Zero;
if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_ADJUST_PRIVILEGES, ref LoggedInUserToken))
{
WriteToLog("OpenProcessToken failed: " + Marshal.GetLastWin32Error());
return false;
}
else
{
//Below part for increasing the UAC previleges to the token.
TokPriv1Luid tp = new TokPriv1Luid();
tp.Count = 1;
tp.Luid = 0;
if (!LookupPrivilegeValue(null, SE_INCREASE_QUOTA_NAME, ref tp.Luid))
{
WriteToLog("LookupPrivilegeValue failed: " + Marshal.GetLastWin32Error());
return false;
}
tp.Attr = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(LoggedInUserToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
{
WriteToLog("OpenProcessToken failed: " + Marshal.GetLastWin32Error());
return false;
}
CloseHandle(LoggedInUserToken);
}
List<Process> explorerProcessList = new List<Process>();
string trayProcessName = AppName.Substring(AppName.LastIndexOf(#"\") + 1, AppName.Length - AppName.LastIndexOf(#"\") - 5);
foreach (Process explorerProcess in Process.GetProcessesByName("explorer"))
{
bool IsProcessRunningForUser = false;
foreach (Process PHTrayProcess in Process.GetProcessesByName(trayProcessName))
{
if (explorerProcess.SessionId == PHTrayProcess.SessionId)
{
if (log.IsDebugEnabled) log.Debug(trayProcessName + " is already running for user SessionId " + explorerProcess.SessionId);
IsProcessRunningForUser = true;
break;
}
}
if (((Environment.OSVersion.Version.Major > 5 && explorerProcess.SessionId > 0)
|| Environment.OSVersion.Version.Major == 5)
&& !IsProcessRunningForUser)
{
if (log.IsDebugEnabled) log.Debug(trayProcessName + " is not running for user SessionId " + explorerProcess.SessionId);
explorerProcessList.Add(explorerProcess);
}
}
if (null != explorerProcessList && explorerProcessList.Count > 0)
{
foreach (Process explorerProcess in explorerProcessList)
{
Process ShellProcess = explorerProcess;
ShellProcess.StartInfo.LoadUserProfile = true;
try
{
int tokenRights = MAXIMUM_ALLOWED; //TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
if (!OpenProcessToken(ShellProcess.Handle, tokenRights, ref ShellProcessToken))
{
WriteToLog("Unable to OpenProcessToken " + Marshal.GetLastWin32Error());
return false;
}
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = Marshal.SizeOf(sa);
if (!DuplicateTokenEx(ShellProcessToken, tokenRights, ref sa, 2, 1, ref DuplicateToken))
{
WriteToLog("Unable to duplicate token " + Marshal.GetLastWin32Error());
return false;
}
WriteToLog("Duplicated the token " + WindowsIdentity.GetCurrent().Name);
SECURITY_ATTRIBUTES processAttributes = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
PROCESS_INFORMATION pi;
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
IntPtr UserEnvironment = IntPtr.Zero;
uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
if (!CreateEnvironmentBlock(out UserEnvironment, ShellProcessToken, true))
{
WriteToLog("Unable to create user's enviroment block " + Marshal.GetLastWin32Error());
}
else
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
//string userName = getUserName(UserEnvironment);
//WriteToLog("UserName:::" + userName);
if (!CreateProcessAsUser(DuplicateToken, AppName, (CmdLineArgs == null) ? string.Empty : CmdLineArgs, ref processAttributes, ref threadAttributes, true, dwCreationFlags, UserEnvironment, AppName.Substring(0, AppName.LastIndexOf('\\')), ref si, out pi))
{
WriteToLog("Unable to create process " + Marshal.GetLastWin32Error());
if (Marshal.GetLastWin32Error() == 740)
{
WriteToLog("Please check the installation as some elevated permissions is required to execute the binaries");
}
return false;
}
Process trayApp = Process.GetProcessById(Convert.ToInt32(pi.dwProcessId));
trayApp.StartInfo.LoadUserProfile = true;
}
finally
{
if (ShellProcessToken != null) CloseHandle(ShellProcessToken);
if (DuplicateToken != null) CloseHandle(DuplicateToken);
}
}
}
else
{
WriteToLog("No user has been identified to have logged into the system.");
return false;
}
WriteToLog("Finished ExecuteAppAsLoggedOnUser for all users.");
return true;
}
/// <summary>
/// Impersonate the user credentials. This would be required by
/// the service applications to impersonate the logged in user
/// credentials to launch certain applications or applying the
/// power scheme.
/// </summary>
/// <returns>Returns true if the impersonation is successful.</returns>
public static bool ImpersonateUser()
{
// For simplicity I'm using the PID of System here
//if (log.IsDebugEnabled) log.Debug("GetaProcess for Explorer");
Process Pname = GetaProcess("explorer");
//This can be null if no user has not logged into the system.
if (Pname == null) return false;
int pid = Pname.Id;
Process proc = Process.GetProcessById(pid);
if (OpenProcessToken(proc.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, ref hToken)) // != 0)
{
WindowsIdentity newId = new WindowsIdentity(hToken);
//log.Debug(newId.Owner);
try
{
const int SecurityImpersonation = 2;
dupeTokenHandle = DupeToken(hToken,
SecurityImpersonation);
if (IntPtr.Zero == dupeTokenHandle)
{
string s = String.Format("Dup failed {0}, privilege not held",
Marshal.GetLastWin32Error());
throw new Exception(s);
}
impersonatedUser = newId.Impersonate();
return true;
}
finally
{
CloseHandle(hToken);
}
}
else
{
string s = String.Format("OpenProcess Failed {0}, privilege not held", Marshal.GetLastWin32Error());
throw new Exception(s);
}
}
/// <summary>
/// Duplicate the token for user impersonation.
/// </summary>
/// <param name="token">Token to duplicate for impersonation</param>
/// <param name="Level">Impersonation security level, currently hardcored to 2</param>
/// <returns>Returns duplicated token</returns>
public static IntPtr DupeToken(IntPtr token, int Level)
{
IntPtr dupeTokenHandle = IntPtr.Zero;
bool retVal = DuplicateToken(token, Level, ref dupeTokenHandle);
return dupeTokenHandle;
}
/// <summary>
/// Get the process running locally on the machine.
/// If the specified process does not exists, it
/// returns back the current process.
/// </summary>
/// <param name="processname">Process name to get</param>
/// <returns>Returns back the process</returns>
public static Process GetaProcess(string processname)
{
Process[] aProc = Process.GetProcessesByName(processname);
if (aProc.Length > 0) return aProc[0];
else
{
//if (log.IsDebugEnabled) log.Debug("Explorer is not running");
Process currentProcess = Process.GetCurrentProcess();
return currentProcess;
}
}
/// <summary>
/// Roleback the impersonation if applied previously.
/// </summary>
public static void UndoImpersonate()
{
impersonatedUser.Undo();
if (hToken != IntPtr.Zero) CloseHandle(hToken);
if (dupeTokenHandle != IntPtr.Zero) CloseHandle(dupeTokenHandle);
return;
}
}
And then you can just do
Impersonation.ExecuteAppAsLoggedOnUser("applicationName", null);
You can either set Act as part of the operating system in Local security policy, or you can also set the privileges programmatically using LsaAddAccountRights.

Running process before windows logon screen

I tried to run clac.exe process before(or exactly when) windows(7 and xp) logon screen is shown.
I tried the gpedit.msc method(add calc.exe to Startup), onLogin/onLogout works only.
Then I created a new windows service,
code (most of my code from: Running a Form in Windows Logon Screen C#):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Runtime.InteropServices;
namespace WindowsService2
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern int WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
out IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AdjustTokenPrivileges(IntPtr tokenhandle,
[MarshalAs(UnmanagedType.Bool)] bool disableAllPrivileges,
[MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES newstate,
uint bufferlength, IntPtr previousState, IntPtr returnlength);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle,
UInt32 DesiredAccess, out IntPtr TokenHandle);
public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
public const UInt32 TOKEN_DUPLICATE = 0x0002;
public const UInt32 TOKEN_IMPERSONATE = 0x0004;
public const UInt32 TOKEN_QUERY = 0x0008;
public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
public const UInt32 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);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public UInt32 PrivilegeCount;
public LUID Luid;
public UInt32 Attributes;
public LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool LookupPrivilegeValue(string lpSystemName, string lpName,
out LUID lpLuid);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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;
}
[Flags]
enum CreateProcessFlags : uint
{
DEBUG_PROCESS = 0x00000001,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
CREATE_SUSPENDED = 0x00000004,
DETACHED_PROCESS = 0x00000008,
CREATE_NEW_CONSOLE = 0x00000010,
NORMAL_PRIORITY_CLASS = 0x00000020,
IDLE_PRIORITY_CLASS = 0x00000040,
HIGH_PRIORITY_CLASS = 0x00000080,
REALTIME_PRIORITY_CLASS = 0x00000100,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_FORCEDOS = 0x00002000,
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
INHERIT_PARENT_AFFINITY = 0x00010000,
INHERIT_CALLER_PRIORITY = 0x00020000,
CREATE_PROTECTED_PROCESS = 0x00040000,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000,
PROCESS_MODE_BACKGROUND_END = 0x00200000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NO_WINDOW = 0x08000000,
PROFILE_USER = 0x10000000,
PROFILE_KERNEL = 0x20000000,
PROFILE_SERVER = 0x40000000,
CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000,
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public UInt32 Attributes;
}
public class RunProcess
{
public void log(string error)
{
throw new Exception(error);
}
public void StartProcess(string pathOrName)
{
Process winLogon = null;
foreach (Process p in Process.GetProcesses())
{
if (p.ProcessName.Contains("winlogon"))
{
winLogon = p;
break;
}
}
// grab the winlogon's token
IntPtr userToken = IntPtr.Zero;
if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken))
{
log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
}
WTSQueryUserToken((uint)WTSGetActiveConsoleSessionId(), out userToken);
// create a new token
IntPtr newToken = IntPtr.Zero;
SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
// duplicate the winlogon token to the new token
if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenImpersonation, out newToken))
{
log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
}
TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
tokPrivs.PrivilegeCount = 1;
LUID seDebugNameValue = new LUID();
if (!LookupPrivilegeValue(null, "SeDebugPrivilege", out seDebugNameValue))
{
log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
}
tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = 0x00000002;
if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero))
{
log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
}
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "Winsta0\\Winlogon";
// start the process using the new token
if (!CreateProcessAsUser(newToken, "calc.exe", null, ref tokenAttributes, ref threadAttributes,true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,"C:\\Windows\\System32", ref si, out pi))
{
log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
}
Process _p = Process.GetProcessById(pi.dwProcessId);
if (_p != null)
{
log("Process " + _p.Id + " Name " + _p.ProcessName);
}
else
{
log("Not Found");
}
}
}
RunProcess r = new RunProcess();
protected override void OnStart(string[] args)
{
r.StartProcess("calc.exe");
}
protected override void OnStop()
{
r.log("Stoped");
}
}
}
I installed the service by this line:
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe" "c:\myservice.exe"
and now I did restart.
And then I checked if the process in running when the logon screen is shown and then I found out that there is no calculator window and there is no process named calc running.
And then I saw that "WindowsService2" is not running (on the taskmgr).
So I think that it can be fixed (I will not give up so fast, i have to finish it today), the question is:
Why its not working, what i need to do now to check why its not working?
I prefer:
There is any other way(or ways) to run process before logon screen(XP and Windows 7)?
(good answer is good example plus good explanation) :)
Edit:
the solution is in the installtion,
the installation log:
Installing assembly 'c:\WindowsService2.exe'.
Affected parameters are:
logtoconsole =
logfile = c:\WindowsService2.InstallLog
assemblypath = c:\WindowsService2.exe
No public installers with the RunInstallerAttribute.Yes attribute could be found in the c:\WindowsService2.exe assembly.
Committing assembly 'c:\WindowsService2.exe'.
Affected parameters are:
logtoconsole =
logfile = c:\WindowsService2.InstallLog
assemblypath = c:\WindowsService2.exe
No public installers with the RunInstallerAttribute.Yes attribute could be found in the c:\WindowsService2.exe assembly.
Remove InstallState file because there are no installers.
cant install because:
No public installers with the RunInstallerAttribute.Yes attribute could be found in the c:\WindowsService2.exe assembly.
what should i do now? :S
(i tried public static readonly RunInstallerAttribute Yes; and i even tried to run the cmd.exe as administrator)
to start your application before start up there are three options:
you can add a new registry value by using mscorlib.dll(RegistryKey Class) functions and classes:
RegistryKey add = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
add.SetValue("Your App Name", "\"" + Application.ExecutablePath.ToString() + "\"");
other locations:
HKLM\Software\Microsoft\Windows\CurrentVersion\Run
HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices
HKLM\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnceEx
you can add youre application manually:
gpedit.msc->windows settings->script files(startup/shutdown)->startup->add..(choose youre application assembly file)
(all gpedit values are saved in the registry, you can also add your application assembly path by using GPMGMTLib(to make youre application more convenient))
you can move or copy youre application assembly file to:
Environment.GetFolderPath(Environment.SpecialFolder.Startup)

Viewing export table on an unmanaged dll in C#

I am currently trying to create a C# application that will allow me to view the export table from an unmanaged DLL. My problem is, once I have all the pointers I need, I have no idea how to loop through the information that the API has provided me. Here is what I have right now:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace DLLMapper
{
class DLLExportViewer
{
#region APIs
[DllImport("imagehlp.dll")]
public static extern Boolean MapAndLoad(String ImageName, String DllPath, ref LOADED_IMAGE LoadedImage, Boolean DotDll, Boolean ReadOnly);
[DllImport("imagehlp.dll")]
public static extern Boolean UnMapAndLoad(ref LOADED_IMAGE LoadedImage);
[DllImport("dbghelp.dll")]
public static extern IntPtr ImageDirectoryEntryToData(IntPtr Base, Boolean MappedAsImage, UInt16 DirectoryEntry, ref Int32 Size);
[DllImport("dbghelp.dll")]
public static extern IntPtr ImageRvaToVa(ref IMAGE_NT_HEADERS NtHeaders, IntPtr Base, UInt32 Rva, ref IMAGE_SECTION_HEADER LastRvaSection);
#endregion
#region Structures
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct LOADED_IMAGE
{
public String ModuleName;
public IntPtr hFile;
public IntPtr MappedAddress;
public IMAGE_NT_HEADERS FileHeader;
public IMAGE_SECTION_HEADER LastRvaSection;
public Int32 NumberOfSections;
public IMAGE_SECTION_HEADER Sections;
public Int32 Characteristics;
public Boolean fSystemImage;
public Boolean fDOSImage;
public LIST_ENTRY Links;
public Int32 SizeOfImage;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions;
public UInt32 AddressOfNames;
public UInt32 AddressOfOrdinals;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_NT_HEADERS
{
public Int32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER OptionalHeader;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct LIST_ENTRY
{
public IntPtr Flink;
public IntPtr Blink;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_SECTION_HEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_SIZEOF_SHORT_NAME)]
public Byte[] Name;
public Misc Misc;
public UInt32 PhysicalAddress;
public UInt32 VirtualAddress;
public UInt32 SizeOfRawData;
public UInt32 PointerToRawData;
public UInt32 PointerToRelocations;
public UInt32 PointerToLinenumbers;
public Int16 NumberOfRelocations;
public Int16 NumberOfLinenumbers;
public UInt32 Characteristics;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_FILE_HEADER
{
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_OPTIONAL_HEADER
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_NUMBEROF_DIRECTORY_ENTRIES)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_DATA_DIRECTORY
{
public UInt32 VirtualAddress;
public UInt32 Size;
}
[StructLayout(LayoutKind.Explicit)]
public struct Misc
{
[FieldOffset(0)]
public UInt32 PhysicalAddress;
[FieldOffset(0)]
public UInt32 VirtualSize;
}
#endregion
#region Variables & Constants
public const Int32 IMAGE_SIZEOF_SHORT_NAME = 8;
public const Int32 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
public const UInt16 IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
/// <summary>
/// String value holding the path to the DLL file. This value is also returned by the FileName property.
/// </summary>
private String sDLLFilePath;
/// <summary>
/// Boolean value that is return by the LibraryLoaded property.
/// </summary>
private Boolean bLibraryLoaded;
/// <summary>
/// Int32 value that is returned by the FunctionCount property.
/// </summary>
private Int32 iFunctionCount;
/// <summary>
/// Int32 value that is returned by the SizeOfImage property.
/// </summary>
private Int32 iSizeOfCode;
/// <summary>
/// String array value that is returned by the ImageFunctions property.
/// </summary>
private String[] sFunctions;
#endregion
#region Properties
/// <summary>
/// Gets a boolean value indicating if the library has been loaded successfully.
/// </summary>
public Boolean LibraryLoaded { get { return bLibraryLoaded; } }
/// <summary>
/// Gets a string value indicating what file the class was initialized with.
/// </summary>
public String FileName { get { return sDLLFilePath; } }
/// <summary>
/// Gets a string array of the functions within the image.
/// </summary>
public String[] ImageFunctions { get { return sFunctions; } }
/// <summary>
/// Gets an Int32 value indicating the number of functions within the image.
/// </summary>
public Int32 FunctionCount { get { return iFunctionCount; } }
#endregion
/// <summary>
/// Initilizes the DLLExportViewer class.
/// </summary>
/// <param name="sFilePath">Path to the DLL file to initilize the class with.</param>
public DLLExportViewer(String sFilePath)
{
IMAGE_SECTION_HEADER ishSectionHeader = new IMAGE_SECTION_HEADER();
LOADED_IMAGE liLoadedImage = new LOADED_IMAGE();
IMAGE_EXPORT_DIRECTORY iedExportDirectory;
IntPtr pImageExportDirectory;
IntPtr pVirtualAddressOfNames;
Int32 iDirectoryExportSize = 0;
sDLLFilePath = sFilePath;
if (MapAndLoad(sDLLFilePath, null, ref liLoadedImage, true, true))
{
bLibraryLoaded = true;
pImageExportDirectory = ImageDirectoryEntryToData(liLoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, ref iDirectoryExportSize);
iedExportDirectory = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(pImageExportDirectory, typeof(IMAGE_EXPORT_DIRECTORY));
iFunctionCount = (Int32)iedExportDirectory.NumberOfFunctions;
pVirtualAddressOfNames = ImageRvaToVa(ref liLoadedImage.FileHeader, liLoadedImage.MappedAddress, iedExportDirectory.AddressOfNames, ref ishSectionHeader);
}
else
{
throw new Exception(String.Format("Failed to load library {0}\n\nError Number:{1]\nError:{2}", sDLLFilePath, Marshal.GetLastWin32Error(), new Win32Exception(Marshal.GetLastWin32Error()).Message));
}
}
}
}
My biggest concern is that I may have some of the marshalling on the structures wrong. But, all in all, I'm not sure where to go from here. Can anyone offer me some assistance?
The following shows how to do it using p/invoke to dbghelp. Note that I used a number of declarations from pinvoke.net.
using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
internal static class NativeMethods
{
[Flags]
public enum EFileAccess : uint
{
//
// Standard Section
//
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file & pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file & pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file & directory
FILE_WRITE_EA = 0x0010, // file & directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
//
// Generic Section
//
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[Flags]
public enum FileMapProtection : uint
{
PageReadonly = 0x02,
PageReadWrite = 0x04,
PageWriteCopy = 0x08,
PageExecuteRead = 0x20,
PageExecuteReadWrite = 0x40,
SectionCommit = 0x8000000,
SectionImage = 0x1000000,
SectionNoCache = 0x10000000,
SectionReserve = 0x4000000,
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFileMapping(
SafeFileHandle hFile,
IntPtr lpFileMappingAttributes,
FileMapProtection flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
string lpName
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFileMapping(
SafeFileHandle hFile,
IntPtr lpFileMappingAttributes,
FileMapProtection flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
IntPtr lpName
);
[Flags]
public enum FileMapAccess : uint
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
FileMapExecute = 0x0020,
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
SafeFileHandle hFileMappingObject,
FileMapAccess dwDesiredAccess,
UInt32 dwFileOffsetHigh,
UInt32 dwFileOffsetLow,
UIntPtr dwNumberOfBytesToMap
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_FILE_HEADER
{
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_DATA_DIRECTORY
{
public UInt32 VirtualAddress;
public UInt32 Size;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_OPTIONAL_HEADER
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_NT_HEADERS
{
public UInt32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER OptionalHeader;
}
[DllImport("dbghelp.dll", SetLastError = true)]
public static extern IntPtr ImageNtHeader(
IntPtr ImageBase
);
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions; // RVA from base of image
public UInt32 AddressOfNames; // RVA from base of image
public UInt32 AddressOfNameOrdinals; // RVA from base of image
}
[DllImport("dbghelp.dll", SetLastError = true)]
public static extern IntPtr ImageRvaToVa(
IntPtr NtHeaders,
IntPtr Base,
UInt32 Rva,
IntPtr LastRvaSection
);
}
namespace ConsoleApplication1
{
class Program
{
private static string[] GetExports(string ModuleFileName)
{
SafeFileHandle FileHandle = NativeMethods.CreateFile(
ModuleFileName,
NativeMethods.EFileAccess.GenericRead,
NativeMethods.EFileShare.Read,
IntPtr.Zero,
NativeMethods.ECreationDisposition.OpenExisting,
NativeMethods.EFileAttributes.Normal,
IntPtr.Zero
);
if (FileHandle.IsInvalid)
throw new Win32Exception();
try
{
SafeFileHandle ImageHandle = NativeMethods.CreateFileMapping(
FileHandle,
IntPtr.Zero,
NativeMethods.FileMapProtection.PageReadonly,
0,
0,
IntPtr.Zero
);
if (ImageHandle.IsInvalid)
throw new Win32Exception();
try
{
IntPtr ImagePointer = NativeMethods.MapViewOfFile(
ImageHandle,
NativeMethods.FileMapAccess.FileMapRead,
0,
0,
UIntPtr.Zero
);
if (ImagePointer == IntPtr.Zero)
throw new Win32Exception();
try
{
IntPtr HeaderPointer = NativeMethods.ImageNtHeader(ImagePointer);
if (HeaderPointer == IntPtr.Zero)
throw new Win32Exception();
NativeMethods.IMAGE_NT_HEADERS Header = (NativeMethods.IMAGE_NT_HEADERS)Marshal.PtrToStructure(
HeaderPointer,
typeof(NativeMethods.IMAGE_NT_HEADERS)
);
if (Header.Signature != 0x00004550)// "PE\0\0" as a DWORD
throw new Exception(ModuleFileName + " is not a valid PE file");
IntPtr ExportTablePointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
Header.OptionalHeader.DataDirectory[0].VirtualAddress,
IntPtr.Zero
);
if (ExportTablePointer == IntPtr.Zero)
throw new Win32Exception();
NativeMethods.IMAGE_EXPORT_DIRECTORY ExportTable = (NativeMethods.IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(
ExportTablePointer,
typeof(NativeMethods.IMAGE_EXPORT_DIRECTORY)
);
IntPtr NamesPointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
ExportTable.AddressOfNames,
IntPtr.Zero
);
if (NamesPointer == IntPtr.Zero)
throw new Win32Exception();
NamesPointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
(UInt32)Marshal.ReadInt32(NamesPointer),
IntPtr.Zero
);
if (NamesPointer == IntPtr.Zero)
throw new Win32Exception();
string[] exports = new string[ExportTable.NumberOfNames];
for (int i = 0; i < exports.Length; i++)
{
exports[i] = Marshal.PtrToStringAnsi(NamesPointer);
NamesPointer += exports[i].Length + 1;
}
return exports;
}
finally
{
if (!NativeMethods.UnmapViewOfFile(ImagePointer))
throw new Win32Exception();
}
}
finally
{
ImageHandle.Close();
}
}
finally
{
FileHandle.Close();
}
}
static void Main(string[] args)
{
foreach (string s in GetExports(#"C:\Windows\System32\kernel32.dll"))
{
Console.WriteLine(s);
}
Console.ReadLine();
}
}
}
Instead of using the API, I just wrote a class that would parse the PE Header to find the location of the export table. I used the PE file format specification from Microsoft to do this.
Microsoft Portable Executable and Common Object File Format Specification

Start a windows service and launch cmd

Do I need to enable Interactive desktp for it to work and what is the correct code to start an EXE or cmd window? I'm still unable to start the service even when I had enable it to interact with desktop.
I would be using an chat engine so it is easier to manage as a windows service.
What wrong with my code?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
namespace MyNewService
{
class Program : ServiceBase
{
static void Main(string[] args)
{
}
public Program()
{
this.ServiceName = "Chatter";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
ThreadStart starter = new ThreadStart(bw_DoWork);
Thread t = new Thread(starter);
t.Start();
}
private void bw_DoWork()
{
Process p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\Windows\system32\cmd.exe");
p.Start();
p.WaitForExit();
base.Stop();
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
}
I have gone through all the pain of doing this.
Under windows 7/Vista/2008 it is not possible to load any interactive process from a service - without calling a number of Win API. = BLACK MAGIC
Have a look here and here.
The code below does the trick, use it with your own risk:
public static class ProcessAsCurrentUser
{
/// <summary>
/// Connection state of a session.
/// </summary>
public enum ConnectionState
{
/// <summary>
/// A user is logged on to the session.
/// </summary>
Active,
/// <summary>
/// A client is connected to the session.
/// </summary>
Connected,
/// <summary>
/// The session is in the process of connecting to a client.
/// </summary>
ConnectQuery,
/// <summary>
/// This session is shadowing another session.
/// </summary>
Shadowing,
/// <summary>
/// The session is active, but the client has disconnected from it.
/// </summary>
Disconnected,
/// <summary>
/// The session is waiting for a client to connect.
/// </summary>
Idle,
/// <summary>
/// The session is listening for connections.
/// </summary>
Listening,
/// <summary>
/// The session is being reset.
/// </summary>
Reset,
/// <summary>
/// The session is down due to an error.
/// </summary>
Down,
/// <summary>
/// The session is initializing.
/// </summary>
Initializing
}
[StructLayout(LayoutKind.Sequential)]
class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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;
}
enum LOGON_TYPE
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK,
LOGON32_LOGON_BATCH,
LOGON32_LOGON_SERVICE,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT,
LOGON32_LOGON_NEW_CREDENTIALS
}
enum LOGON_PROVIDER
{
LOGON32_PROVIDER_DEFAULT,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
[Flags]
enum CreateProcessFlags : uint
{
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000
}
[StructLayout(LayoutKind.Sequential)]
public struct WTS_SESSION_INFO
{
public int SessionID;
[MarshalAs(UnmanagedType.LPTStr)]
public string WinStationName;
public ConnectionState State;
}
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int32 WTSEnumerateSessions(IntPtr hServer, int reserved, int version,
ref IntPtr sessionInfo, ref int count);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
UInt32 dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("kernel32.dll")]
private static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern int WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
int ImpersonationLevel,
int TokenType,
out IntPtr phNewToken);
private const int TokenImpersonation = 2;
private const int SecurityIdentification = 1;
private const int MAXIMUM_ALLOWED = 0x2000000;
private const int TOKEN_DUPLICATE = 0x2;
private const int TOKEN_QUERY = 0x00000008;
/// <summary>
/// Launches a process for the current logged on user if there are any.
/// If none, return false as well as in case of
///
/// ##### !!! BEWARE !!! #### ------------------------------------------
/// This code will only work when running in a windows service (where it is really needed)
/// so in case you need to test it, it needs to run in the service. Reason
/// is a security privileg which only services have (SE_??? something, cant remember)!
/// </summary>
/// <param name="processExe"></param>
/// <returns></returns>
public static bool CreateProcessAsCurrentUser(string processExe)
{
IntPtr duplicate = new IntPtr();
STARTUPINFO info = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
Debug.WriteLine(string.Format("CreateProcessAsCurrentUser. processExe: " + processExe));
IntPtr p = GetCurrentUserToken();
bool result = DuplicateTokenEx(p, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE, IntPtr.Zero, SecurityIdentification, SecurityIdentification, out duplicate);
Debug.WriteLine(string.Format("DuplicateTokenEx result: {0}", result));
Debug.WriteLine(string.Format("duplicate: {0}", duplicate));
if (result)
{
result = CreateProcessAsUser(duplicate, processExe, null,
IntPtr.Zero, IntPtr.Zero, false, (UInt32)CreateProcessFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null,
ref info, out procInfo);
Debug.WriteLine(string.Format("CreateProcessAsUser result: {0}", result));
}
if (p.ToInt32() != 0)
{
Marshal.Release(p);
Debug.WriteLine(string.Format("Released handle p: {0}", p));
}
if (duplicate.ToInt32() != 0)
{
Marshal.Release(duplicate);
Debug.WriteLine(string.Format("Released handle duplicate: {0}", duplicate));
}
return result;
}
public static int GetCurrentSessionId()
{
uint sessionId = WTSGetActiveConsoleSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == 0xFFFFFFFF)
return -1;
else
return (int)sessionId;
}
public static bool IsUserLoggedOn()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
Debug.WriteLine(string.Format("Number of sessions: {0}", wtsSessionInfos.Count));
return wtsSessionInfos.Where(x => x.State == ConnectionState.Active).Count() > 0;
}
private static IntPtr GetCurrentUserToken()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
int sessionId = wtsSessionInfos.Where(x => x.State == ConnectionState.Active).FirstOrDefault().SessionID;
//int sessionId = GetCurrentSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == int.MaxValue)
{
return IntPtr.Zero;
}
else
{
IntPtr p = new IntPtr();
int result = WTSQueryUserToken((UInt32)sessionId, out p);
Debug.WriteLine(string.Format("WTSQueryUserToken result: {0}", result));
Debug.WriteLine(string.Format("WTSQueryUserToken p: {0}", p));
return p;
}
}
public static List<WTS_SESSION_INFO> ListSessions()
{
IntPtr server = IntPtr.Zero;
List<WTS_SESSION_INFO> ret = new List<WTS_SESSION_INFO>();
try
{
IntPtr ppSessionInfo = IntPtr.Zero;
Int32 count = 0;
Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (int)ppSessionInfo;
if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
current += dataSize;
ret.Add(si);
}
WTSFreeMemory(ppSessionInfo);
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
}
return ret;
}
}
When running as a service you won't be able to launch anything that needs to interact with the desktop OR will spawn it's own windows.
As Aliostad said, you need to call Win API calls to CreateProcessAsUser and emulate the user in order with this to work. This involves emulating the logged in user and using their credentials to "lift" your process into process isolation level 1 (which gives you access to the windowing system and things like the GPU).
I am doing this in an app I wrote and it does work but I agree with Aliostad there is a bit of Black magic going on and it generally sucks
Having said all that, you can spawn worker processes from within a service as along as they don't require things that are in process isolation level 1 (Windowing, GPU etc..)
cmd.exe by default will try to create a window, this is why your example is failing. You could set the following ProcessStartInfo properties to get it work.
CreateNoWindow
WindowStyle
I wrote an application watchdog service which simply restarts an application (in my case a Console Window App).
I found a very good Hands-On Lab Tutorial (in C++) which I tried at it worked for Session 0 Isolation at: http://msdn.microsoft.com/en-us/Windows7TrainingCourse_Win7Session0Isolation
I converted that C++ Sample into C#. After a few tests it worked. As long as I stay logged on and don't log out and log on again that code works perfect. I have to do a little to catch the Session Logout/Login. But for simple log-in and Work Condition in Windows the watchdog works as expected.
Here is that required PInvoke code:
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
public static extern uint WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
out IntPtr phNewToken);
Here is the encapsuled method :
private void CreateUserProcess()
{
bool ret;
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
uint dwSessionID = WTSGetActiveConsoleSessionId();
this.EventLog.WriteEntry("WTSGetActiveConsoleSessionId: " + dwSessionID, EventLogEntryType.FailureAudit);
IntPtr Token = new IntPtr();
ret = WTSQueryUserToken((UInt32)dwSessionID, out Token);
if (ret == false)
{
this.EventLog.WriteEntry("WTSQueryUserToken failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
}
const uint MAXIMUM_ALLOWED = 0x02000000;
IntPtr DupedToken = IntPtr.Zero;
ret = DuplicateTokenEx(Token,
MAXIMUM_ALLOWED,
ref sa,
SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
TOKEN_TYPE.TokenPrimary,
out DupedToken);
if (ret == false)
{
this.EventLog.WriteEntry("DuplicateTokenEx failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
}
else
{
this.EventLog.WriteEntry("DuplicateTokenEx SUCCESS", EventLogEntryType.SuccessAudit);
}
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
//si.lpDesktop = "";
string commandLinePath;
// commandLinePath example: "c:\myapp.exe c:\myconfig.xml" . cmdLineArgs can be ommited
commandLinePath = AppPath + " " + CmdLineArgs;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
//CreateProcessAsUser(hDuplicatedToken, NULL, lpszClientPath, NULL, NULL, FALSE,
// 0,
// NULL, NULL, &si, &pi)
ret = CreateProcessAsUser(DupedToken, null, commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, null, ref si, out pi);
if (ret == false)
{
this.EventLog.WriteEntry("CreateProcessAsUser failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
}
else
{
this.EventLog.WriteEntry("CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId, EventLogEntryType.SuccessAudit);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
ret = CloseHandle(DupedToken);
if (ret == false)
{
this.EventLog.WriteEntry("CloseHandle LastError: " + Marshal.GetLastWin32Error(), EventLogEntryType.Error);
}
else
{
this.EventLog.WriteEntry("CloseHandle SUCCESS", EventLogEntryType.Information);
}
}
I hope it is useful !
The function below will launch an executable as active user from a windows service.
//Function to run a process as active user from windows service
void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args)
{
DWORD session_id = -1;
DWORD session_count = 0;
WTS_SESSION_INFOA *pSession = NULL;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
{
//log success
}
else
{
//log error
return;
}
for (int i = 0; i < session_count; i++)
{
session_id = pSession[i].SessionId;
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned))
{
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
if (wts_connect_state != WTSActive) continue;
}
else
{
//log error
continue;
}
HANDLE hImpersonationToken;
if (!WTSQueryUserToken(session_id, &hImpersonationToken))
{
//log error
continue;
}
//Get real token from impersonation token
DWORD neededSize1 = 0;
HANDLE *realToken = new HANDLE;
if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
else
{
//log error
continue;
}
HANDLE hUserToken;
if (!DuplicateTokenEx(hImpersonationToken,
//0,
//MAXIMUM_ALLOWED,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hUserToken))
{
//log error
continue;
}
// Get user name of this process
//LPTSTR pUserName = NULL;
WCHAR* pUserName;
DWORD user_name_len = 0;
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
{
//log username contained in pUserName WCHAR string
}
//Free memory
if (pUserName) WTSFreeMemory(pUserName);
ImpersonateLoggedOnUser(hUserToken);
STARTUPINFOW StartupInfo;
GetStartupInfoW(&StartupInfo);
StartupInfo.cb = sizeof(STARTUPINFOW);
//StartupInfo.lpDesktop = "winsta0\\default";
PROCESS_INFORMATION processInfo;
SECURITY_ATTRIBUTES Security1;
Security1.nLength = sizeof SECURITY_ATTRIBUTES;
SECURITY_ATTRIBUTES Security2;
Security2.nLength = sizeof SECURITY_ATTRIBUTES;
void* lpEnvironment = NULL;
// Get all necessary environment variables of logged in user
// to pass them to the new process
BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
if (!resultEnv)
{
//log error
continue;
}
WCHAR PP[1024]; //path and parameters
ZeroMemory(PP, 1024 * sizeof WCHAR);
wcscpy(PP, path);
wcscat(PP, L" ");
wcscat(PP, args);
// Start the process on behalf of the current user
BOOL result = CreateProcessAsUserW(hUserToken,
NULL,
PP,
//&Security1,
//&Security2,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
//lpEnvironment,
NULL,
//"C:\\ProgramData\\some_dir",
NULL,
&StartupInfo,
&processInfo);
if (!result)
{
//log error
}
else
{
//log success
}
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(hImpersonationToken);
CloseHandle(hUserToken);
CloseHandle(realToken);
RevertToSelf();
}
WTSFreeMemory(pSession);
}

Categories