• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 780
  • Last Modified:

C# code to remotely exec application for a terminal server user session

I'm  looking for a C# solution to start an application in TS session(s) for specific users.

I have admin rights on the server and also a service I've created for other purposes but I can use it to do the job if necessary.

Currently, I've added the application in the HKCU\....\run. but the problem is that the application will start only once the user will log on to the TS. It will be most efficient if the application will start while the user is logged on and without user interaction.
0
OrenRozen
Asked:
OrenRozen
  • 6
  • 5
2 Solutions
 
jagrut_patelCommented:
Create a system-tray application (if your app has a UI) or as a windows service (if your app has no UI) and install it as "Per User" instead of "Everyone".
0
 
OrenRozenAuthor Commented:
@minhvc:
the second link looks promising.
but I can't get the code to work. I get errors in lines 23 and 32



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace CreateProcessAsUser
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private void CreateProcessAsUser()
        {
            //Error in this line : The name 'WindowsIdentity' does not exist in the current context
            IntPtr hToken = WindowsIdentity.GetCurrent().Token;
            IntPtr hDupedToken = IntPtr.Zero;

            ProcessUtility.PROCESS_INFORMATION pi = new ProcessUtility.PROCESS_INFORMATION();

            try
            {
                //Error in this line: Error	2	The type name 'SECURITY_ATTRIBUTE' does not exist in the
                //type 'CreateProcessAsUser.ProcessUtility'
                ProcessUtility.SECURITY_ATTRIBUTES sa = new ProcessUtility.SECURITY_ATTRIBUTE();
                sa.Length = Marshal.SizeOf(sa);

                bool result = ProcessUtility.DuplicateTokenEx(
                      hToken,
                      ProcessUtility.GENERIC_ALL_ACCESS,
                      ref sa,
                      (int)ProcessUtility.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                      (int)ProcessUtility.TOKEN_TYPE.TokenPrimary,
                      ref hDupedToken
                   );

                if (!result)
                {
                    throw new ApplicationException("DuplicateTokenEx failed");
                }


                ProcessUtility.STARTUPINFO si = new ProcessUtility.STARTUPINFO();
                si.cb = Marshal.SizeOf(si);
                si.lpDesktop = String.Empty;

                result = ProcessUtility.CreateProcessAsUser(
                                     hDupedToken,
                                     @"",
                                     String.Empty,
                                     ref sa, ref sa,
                                     false, 0, IntPtr.Zero,
                                     @"C:\", ref si, ref pi
                               );

                if (!result)
                {
                    int error = Marshal.GetLastWin32Error();
                    string message = String.Format("CreateProcessAsUser Error: {0}", error);
                    throw new ApplicationException(message);
                }
            }
            finally
            {
                if (pi.hProcess != IntPtr.Zero)
                    ProcessUtility.CloseHandle(pi.hProcess);
                if (pi.hThread != IntPtr.Zero)
                    ProcessUtility.CloseHandle(pi.hThread);
                if (hDupedToken != IntPtr.Zero)
                    ProcessUtility.CloseHandle(hDupedToken);
            }
        }
    }

    public class ProcessUtility
    {
        [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;

        [
           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);
    }

}

Open in new window

0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
OrenRozenAuthor Commented:
I've requested that this question be deleted for the following reason:

no solution provided
0
 
jagrut_patelCommented:
OrenRozen, sorry to object your deletion request. I just wanted to share my thought about your issue.

Firstly, it is not advisable to launch an interactive application from a window's service. You may like to study this discussion.

If I have understood your requirement correctly then you wish to start an interactive application (e.g. launch a Form) even before user logs-in. Please correct if my understand is wrong?

I think there is no point in opening a Form if there is no user to interact with (user hasn't logged-in yet) and that is one of the reasons why launching an interactive app from Windows service is not advisable. So please re-consider your application design. Look if you can divide your application logic in two parts; one that can be run without user-interaction can be put into Windows Service while the other as part of Windows Application.

If this is possible then you can create a simple Windows Service app that will start even before user logs-in. The Windows Application part can be added in HKCU/.../run to auto run it when user logs-in. If required then Windows app can interact with Windows Service via WCF Named Pipe binding.
0
 
jagrut_patelCommented:
By mistake I clicked Submit instead of Object button.
0
 
OrenRozenAuthor Commented:
Objection noted. Please note the following:

1. Your advise not to launch an app from a service is noted but not relevent to my problem and question.

2. I want to run the application after user logon and not before.

3. as stated in my initial question, I curently using HKCU\...\run to start my application (but what about users that are already logged on? and thats the main issue!!)

4. You first expert comment here is not relevent to my question.
0
 
jagrut_patelCommented:
Thank you for the clarifications.

I'm still thinking how launching a process with user's identity (CreateProcessAsUser, etc.) can solve your main issue "To automatically run the application for already logged on users."? May be that is needed to prevent login prompts to the user.

Anyway, only approach I can think of is the uncommon way of staring a new process from a simple Windows Service as shown here for calc.exe. The window's service will need special configuration to allow this. As I mentioned previously you will need to separate apps; a windows service and a winforms application.

Here again question will be to know whether user has actually logged-in or not. For that you may have to use P/Invoke or Cassia as discussed on this links:
http://stackoverflow.com/questions/551412/check-if-no-user-is-currently-logged-on-to-windows
http://code.google.com/p/cassia/

Your windows service can intermittently check whether user is logged-in. If so then it can periodically check whether your Windows Application is running. If not then it can start the Windows application exe using ProcessStartInfo.

If you think this won't work you may re-submit question deletion request as at least I don't have any other idea to share.
0
 
OrenRozenAuthor Commented:
how can I use ProcessStartInfo to start a process from a service for a specific user?
0
 
jagrut_patelCommented:
Okay, understood. CreateProcessAsUser, etc. is needed for that. ProcessStartInfo won't help.
Code shown in @minhvc suggested second link should be made working.
0
 
OrenRozenAuthor Commented:
look at my comment regarding the second link. I've attached a code but having problem with.
can you assist with the exceptions in this code?
0
 
jagrut_patelCommented:
For the first error "//Error in this line : The name 'WindowsIdentity' does not exist in the current context...." include following namespace at the top of the file along with other using statements.

using System.Security.Principal;


For the second error "Error in this line: Error      2      The type name 'SECURITY_ATTRIBUTE' does not exist in the ....." instead of

ProcessUtility.SECURITY_ATTRIBUTES sa = new ProcessUtility.SECURITY_ATTRIBUTE();

write

ProcessUtility.SECURITY_ATTRIBUTES sa = new ProcessUtility.SECURITY_ATTRIBUTES();

You misspelled SECURITY_ATTRIBUTES.
0

Featured Post

Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

  • 6
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now