Service Starting a Process Wont Show Gui C#

Service starting a process wont show GUI C#

This article explains Session 0 Isolation which among other things disallows services from creating a UI in Windows Vista/7. In your service starts another process, it starts in Session 0 and also will not show any UI. (By the way, the UI is created, it's just that Session 0 is never displayed). This article on CodeProject can help you create a process from a service on the user's desktop and show its UI.

Also, please consider wrapping you stream objects in a using statement so that they are properly disposed.

UI of Process is not visible after Process.Start()

That is most likely because your Windows Service is not in user interactive mode.

You have to enable this from the Services panel, as described in this blog: Check the Allow service to interact with desktop in the service properties Log On page.

Also read Microsofts recommendations on user interactive services.

Starting process programatically in C# from windows service, UI does not show up i see the process in task manager

Services don't have a desktop associated with them, so when you start the GUI application, it will not show up to the user.

You need to configure the service to have interaction with the desktop, then it will work. You can do this either diagrammatically in the service installer class

Or manually in the service properties windows, under the Log On tab.

C# Windows Service Creates Process but doesn't executes it

So the only alternative to this is to trick the OS. And make an instance of the Process from the Kernel but changing who was the creator. Let me elaborate.

Since Windows Vista and greater...Microsoft thought that having the Windows Service as a Service that can interactive with User GUI was a bad idea(and I agree at some point) because it may be potentially a virus that will run everytime at startup. So they created something called a Session 0. All your services are in this Session so that they are not able to interact with your user(or Session 1 +) GUI. Meaning the Windows Service has no access to cmd.exe, VBoxManage.exe, any other app that has GUI interaction.

So... the solution to the problem is tricking the OS, creating the Process from the Kernel with Platform Invokes(Win 32 API) which is not that common for a day to day developer in C#.
When creating the Process from the KernelDLL you have access to change who the User or the Creator is. In this case instead of having the Session 0 creating the Process, I changed it to the current Session ID, or current User. This made it possible for my Windows Service Work like I wanted.

For this idea to work you have to read a lot about KernelDll, advapi32.dll, mostly their methods and enum declarations since its not something you can just reference into your project. Those two need to be P/Invoke in order to use them.

The Following Class that I created makes it possible for you to create a process as the current user and not as Session 0. Hence solving my original problem.

//Just use the Class Method no need to instantiate it:
ApplicationLoader.CreateProcessAsUser(string filename, string args)

[SuppressUnmanagedCodeSecurity]
class ApplicationLoader
{
/// <summary>
/// No Need to create the class.
/// </summary>
private ApplicationLoader() { }

enum TOKEN_INFORMATION_CLASS
{

TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}

[StructLayout(LayoutKind.Sequential)]
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 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)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessID;
public Int32 dwThreadID;
}

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}

public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}

public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}

public const int GENERIC_ALL_ACCESS = 0x10000000;
public const int CREATE_NO_WINDOW = 0x08000000;

[DllImport("advapi32.dll", EntryPoint = "ImpersonateLoggedOnUser", SetLastError = true,
CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr ImpersonateLoggedOnUser(IntPtr hToken);

[
DllImport("kernel32.dll",
EntryPoint = "CloseHandle", SetLastError = true,
CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)
]
public static extern bool CloseHandle(IntPtr handle);

[
DllImport("advapi32.dll",
EntryPoint = "CreateProcessAsUser", SetLastError = true,
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)
]
public static extern bool
CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment,
string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
ref PROCESS_INFORMATION lpProcessInformation);

[
DllImport("advapi32.dll",
EntryPoint = "DuplicateTokenEx")
]
public static extern bool
DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel, Int32 dwTokenType,
ref IntPtr phNewToken);

[DllImport("Kernel32.dll", SetLastError = true)]
//[return: MarshalAs(UnmanagedType.U4)]
public static extern IntPtr WTSGetActiveConsoleSessionId();

[DllImport("advapi32.dll")]
public static extern IntPtr SetTokenInformation(IntPtr TokenHandle, IntPtr TokenInformationClass, IntPtr TokenInformation, IntPtr TokenInformationLength);

[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSQueryUserToken(uint sessionId, out IntPtr Token);

private static int getCurrentUserSessionID()
{
uint dwSessionId = (uint)WTSGetActiveConsoleSessionId();

//Gets the ID of the User logged in with WinLogOn
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
if ((uint)p.SessionId == dwSessionId)
{

//this is the process controlled by the same sessionID
return p.SessionId;
}
}

return -1;
}

/// <summary>
/// Actually calls and creates the application.
/// </summary>
/// <param name="filename"></param>
/// <param name="args"></param>
/// <returns></returns>
public static Process CreateProcessAsUser(string filename, string args)
{
//var replaces IntPtr
var hToken = WindowsIdentity.GetCurrent().Token; //gets Security Token of Current User.

var hDupedToken = IntPtr.Zero;

var pi = new PROCESS_INFORMATION();
var sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);

try
{
if (!DuplicateTokenEx(
hToken,
GENERIC_ALL_ACCESS,
ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary,
ref hDupedToken
))
throw new Win32Exception(Marshal.GetLastWin32Error());

var si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "";

var path = Path.GetFullPath(filename);
var dir = Path.GetDirectoryName(path);

//Testing
uint curSessionid = (uint)ApplicationLoader.getCurrentUserSessionID();

if (!WTSQueryUserToken(curSessionid,out hDupedToken))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

// Revert to self to create the entire process; not doing this might
// require that the currently impersonated user has "Replace a process
// level token" rights - we only want our service account to need
// that right.
using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero))
{
if (!CreateProcessAsUser(
hDupedToken,
path,
string.Format("\"{0}\" {1}", filename.Replace("\"", "\"\""), args),
ref sa, ref sa,
false, CREATE_NO_WINDOW, IntPtr.Zero,
dir, ref si, ref pi
))
throw new Win32Exception(Marshal.GetLastWin32Error());
}

return Process.GetProcessById(pi.dwProcessID);
}
finally
{
if (pi.hProcess != IntPtr.Zero)
CloseHandle(pi.hProcess);
if (pi.hThread != IntPtr.Zero)
CloseHandle(pi.hThread);
if (hDupedToken != IntPtr.Zero)
CloseHandle(hDupedToken);
}
}
}

Modify the class at your will. Just be careful not to touch a lot of the initial enum declarations or the external methods if you have no clue how those work yet.

C# GUI starting service not working when Switch User is selected from Lock Screen

Found a Workaround, using the Standard

CanHandleSessionChangeEvent = true

and overriding the default Session Events handling function. I recommend using these instead of the ISensLogon Interface as they require a great deal less code.
A good example can be found in the official Microsoft documentation: SessionChangeDescription Structure.

I hope someone finds this helpful, I spent hours trying to get the above code working!



Related Topics



Leave a reply



Submit