Launching an App as a user account (context) from a Service

https://blog.cjwdev.co.uk/2011/06/10/vb-net-start-process-in-console-session-from-windows-service-on-windows-7/

' Usage of it, followed by the WindowsApi class

Dim UserTokenHandle As IntPtr = IntPtr.Zero
WindowsApi.WTSQueryUserToken(WindowsApi.WTSGetActiveConsoleSessionId, UserTokenHandle)
Dim ProcInfo As New WindowsApi.PROCESS_INFORMATION
Dim StartInfo As New WindowsApi.STARTUPINFOW

StartInfo.cb = CUInt(Runtime.InteropServices.Marshal.SizeOf(StartInfo))

WindowsApi.CreateProcessAsUser(UserTokenHandle, "C:\Windows\System32\cmd.exe", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, False0, IntPtr.Zero, Nothing, StartInfo, ProcInfo)
If Not UserTokenHandle = IntPtr.Zero Then
    WindowsApi.CloseHandle(UserTokenHandle)
End If


Imports System.Runtime.InteropServices
Public Class WindowsApi
    <DllImport("kernel32.dll", EntryPoint:="WTSGetActiveConsoleSessionId", SetLastError:=True)> _
    Public Shared Function WTSGetActiveConsoleSessionId() As UInteger
    End Function
    <DllImport("Wtsapi32.dll", EntryPoint:="WTSQueryUserToken", SetLastError:=True)> _
    Public Shared Function WTSQueryUserToken(ByVal SessionId As UInteger, ByRef phToken As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("kernel32.dll", EntryPoint:="CloseHandle", SetLastError:=True)> _
    Public Shared Function CloseHandle(<InAttribute()> ByVal hObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("advapi32.dll", EntryPoint:="CreateProcessAsUserW", SetLastError:=True)> _
    Public Shared Function CreateProcessAsUser(<InAttribute()> ByVal hToken As IntPtr, _
                                                    <InAttribute(), MarshalAs(UnmanagedType.LPWStr)> ByVal lpApplicationName As String, _
                                                    ByVal lpCommandLine As System.IntPtr, _
                                                    <InAttribute()> ByVal lpProcessAttributes As IntPtr, _
                                                    <InAttribute()> ByVal lpThreadAttributes As IntPtr, _
                                                    <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandles As Boolean, _
                                                    ByVal dwCreationFlags As UInteger, _
                                                    <InAttribute()> ByVal lpEnvironment As IntPtr, _
                                                    <InAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpCurrentDirectory As String, _
                                                    <InAttribute()> ByRef lpStartupInfo As STARTUPINFOW, _
                                                    <OutAttribute()> ByRef lpProcessInformation As PROCESS_INFORMATION) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure SECURITY_ATTRIBUTES
        Public nLength As UInteger
        Public lpSecurityDescriptor As IntPtr
        <MarshalAs(UnmanagedType.Bool)> _
        Public bInheritHandle As Boolean
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure STARTUPINFOW
        Public cb As UInteger
        <MarshalAs(UnmanagedType.LPWStr)> _
        Public lpReserved As String
        <MarshalAs(UnmanagedType.LPWStr)> _
        Public lpDesktop As String
        <MarshalAs(UnmanagedType.LPWStr)> _
        Public lpTitle As String
        Public dwX As UInteger
        Public dwY As UInteger
        Public dwXSize As UInteger
        Public dwYSize As UInteger
        Public dwXCountChars As UInteger
        Public dwYCountChars As UInteger
        Public dwFillAttribute As UInteger
        Public dwFlags As UInteger
        Public wShowWindow As UShort
        Public cbReserved2 As UShort
        Public lpReserved2 As IntPtr
        Public hStdInput As IntPtr
        Public hStdOutput As IntPtr
        Public hStdError As IntPtr
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure PROCESS_INFORMATION
        Public hProcess As IntPtr
        Public hThread As IntPtr
        Public dwProcessId As UInteger
        Public dwThreadId As UInteger
    End Structure
End Class
<DllImport("Userenv.dll", EntryPoint:="CreateEnvironmentBlock", SetLastError:=True)> _
Public Shared Function CreateEnvironmentBlock( ByRef lpEnvironment As IntPtr, ByVal hToken As IntPtr, ByVal bInherit As Boolean) As Boolean
End Function
Public Shared Function CreateEnvironmentBlock( ByRef lpEnvironment As IntPtr, ByVal hToken As IntPtr, ByVal bInherit As Boolean) As Boolean
End Function

' This is if you want to pass command line params

Adding in Command Line Args


Have to update the library reference

<DllImport("advapi32.dll", EntryPoint:="CreateProcessAsUserW", SetLastError:=True)>
    Public Shared Function CreateProcessAsUser(<InAttribute()> ByVal hToken As IntPtr,
                                                    <InAttribute(), MarshalAs(UnmanagedType.LPWStr)> ByVal lpApplicationName As String,
                                                <InAttribute(), MarshalAs(UnmanagedType.LPWStr)> ByVal lpCommandLine As String,
                                                    <InAttribute()> ByVal lpProcessAttributes As IntPtr,
                                                    <InAttribute()> ByVal lpThreadAttributes As IntPtr,
                                                    <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandles As Boolean,
                                                    ByVal dwCreationFlags As UInteger,
                                                    <InAttribute()> ByVal lpEnvironment As IntPtr,
                                                    <InAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpCurrentDirectory As String,
                                                    <InAttribute()> ByRef lpStartupInfo As STARTUPINFOW,
                                                    <OutAttribute()> ByRef lpProcessInformation As PROCESS_INFORMATION) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function ' 'ByVal lpCommandLine As System.IntPtr,

 
CreateEnvironmentBlock
' If you need this
Then to use it you just call it:
CreateEnvironmentBlock(EnvironmentBlock, UserTokenHandle, False)
Then pass that pointer in to CreateProcessAsUser (the 4th to last argument) :
CreateProcessAsUser(UserTokenHandle, Nothing, CommandLine, IntPtr.Zero, IntPtr.Zero, False, ApiDefinitions.CREATE_UNICODE_ENVIRONMENT Or ApiDefinitions.CREATE_NEW_CONSOLE, EnvironmentBlock, Nothing, StartInfo, ProcInfo)


Check to see if a Process is running

    Function CheckIfRunning() As Boolean
        p = Process.GetProcessesByName("MDRecords")
        If p.Count > 0 Then
           ' Process is running
            Return True
        Else
            ' Process is not running
            Return False
        End If
    End Function

Example

Private Sub Timer_Tick(sender As Object, e As EventArgs)


        Dim instance As myNewTimer = DirectCast(sender, myNewTimer)

        instance.Stop()
        Dim UserTokenHandle As IntPtr = IntPtr.Zero
        Dim EnvironmentBlock As IntPtr = IntPtr.Zero
        Dim ProcInfo As New WindowsApi.PROCESS_INFORMATION
        Dim StartInfo As New WindowsApi.STARTUPINFOW

        WindowsApi.WTSQueryUserToken(WindowsApi.WTSGetActiveConsoleSessionId, UserTokenHandle)
        'Dim UserTokenHandle As IntPtr = IntPtr.Zero
        'Dim EnvironmentBlock As IntPtr = IntPtr.Zero
        'Dim ProcInfo As New WindowsApi.PROCESS_INFORMATION
        'Dim StartInfo As New WindowsApi.STARTUPINFOW
        StartInfo.cb = CUInt(Runtime.InteropServices.Marshal.SizeOf(StartInfo))
        WindowsApi.CreateEnvironmentBlock(EnvironmentBlock, UserTokenHandle, False)
        'Dim evaluator As New Threading.Thread(Sub() StartTimer("-None-"))


        Debug.Print(Now & "  Tick for: " & instance.IDNumber.ToString)
        If Not CheckIfRunning() Then

            WindowsApi.CreateProcessAsUser(UserTokenHandle, "c:\PROGRA~2\IMPRIV~1\ONESIG~1\ISXRunAs.exe"" /profile c:\CrouseScripts\MDRecords.exe", IntPtr.Zero, IntPtr.Zero, False0, IntPtr.Zero, Nothing, StartInfo, ProcInfo)
            'WindowsApi.CreateProcessAsUser(UserTokenHandle, "c:\crousescripts\MDRecords.exe", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, StartInfo, ProcInfo)
            If Not UserTokenHandle = IntPtr.Zero Then
                WindowsApi.CloseHandle(UserTokenHandle)
            End If
        End If

        instance.Start()


    End Sub

    Dim p() As Process

List Processes For a Session ID

       Dim AllP() As Process = Process.GetProcesses()
        Dim CurP As Process = Process.GetCurrentProcess()
        Dim lst = (From x As Process In AllP Where x.SessionId = CurP.SessionId Select x).ToArray()
        For Each p As Process In lst
            ComboBox1.Items.Add(p.ProcessName)
        Next

Kill Remote Process


Function funTaskKill(sRemoteName As String, sEXE As String, Optional txtDomain As String = "", Optional txtUser As String = "", Optional txtPassword As String = "") As String
        Dim sRet As String = ""

        Dim pProcess As New Process
        pProcess.StartInfo.CreateNoWindow = True
        If Len(txtUser) > 0 Then pProcess.StartInfo.UserName = txtUser
        If Len(txtPassword) > 0 Then pProcess.StartInfo.PasswordInClearText = (txtPassword)
        pProcess.StartInfo.UseShellExecute = False
        pProcess.StartInfo.WorkingDirectory = "c:\windows\system32"
        If Len(txtDomain) > 0 Then pProcess.StartInfo.Domain = txtDomain
        pProcess.StartInfo.FileName = "C:\Windows\System32\taskkill.exe"
        pProcess.StartInfo.Arguments = " /F " & IIf(Len(sRemoteName) > 0, " /S " & sRemoteName & " ", "") & " /IM " & sEXE
        pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        pProcess.StartInfo.RedirectStandardOutput = True
        pProcess.StartInfo.RedirectStandardError = True


        ' Try
        'TextBox1.AppendText("Query  " & servername & " " & Now & vbCrLf)
        pProcess.Start()
        If Not pProcess.WaitForExit(1000 * 10) Then
            'TextBox1.AppendText("60 second timeout on  " & servername & " " & Now & "  Aborting." & vbCrLf)
            pProcess.Kill()
            oLog.WriteEntry(Now, "RestartServicesMPF", sUser, "KillProcess", "Timeout: " & pProcess.StartInfo.FileName & " " & pProcess.StartInfo.Arguments)
            'Return
            sRet = "error: Timeout trying to kill: " & sEXE & " on " & sRemoteName

        Else




            Dim errortext As String = pProcess.StandardError.ReadToEnd.ToString

            If errortext.Length = 0 Then
                Dim sOutputText As String = pProcess.StandardOutput.ReadToEnd.ToString & vbCrLf
                sRet = "success: kill of " & sEXE & " on " & sRemoteName
                oLog.WriteEntry(Now, "RestartServicesMPF", sUser, "KillProcess", "OutputText: " & sOutputText)
            Else 'error text > 0
                oLog.WriteEntry(Now, "RestartServicesMPF", sUser, "KillProcess", "ErrorText: " & errortext)
                sRet = "error: " & errortext
            End If

        End If

        Return sRet

    End Function