7

Actualmente estoy usando un método local para ejecutar un proceso como un usuario diferente en Vista, y no puedo evitar la sensación de que es un hack-ish y menos que ideal (además del hecho de que craps UAC, bloqueando mi aplicación con una excepción de seguridad y forzándome a desactivar el UAC por completo). Mi proceso consta de dos proyectos (por lo que dos archivos EXE) - una "interfaz" y un "talón de lanzamiento" - y aquí está el proceso:Ejecutar nuevo proceso como usuario diferente en vb.net

  1. usuario tiene un acceso directo que pone en marcha "Interface.exe notepad.exe"
  2. Interface.exe tiene una forma que solicita las credenciales que les gustaría usar
  3. Interace.exe utiliza ProcessStartInfo para crear una instancia de LaunchStub.exe (LS) como el nuevo usuario
  4. LS utiliza ProcessStartInfo (con ShellExecute establecido en verdadero) para iniciar el archivo solicitado, y dado que ya se está ejecutando como el usuario solicitado, también lo es el nuevo proceso.

La razón por la que tengo un proceso de dos pasos es que deseo que los usuarios puedan hacer clic derecho en cualquier archivo que el sistema operativo tenga una acción predeterminada para (.EXE, .SQL, .MSC, etc.) y inicie, y ProcessStartInfo solo lo admite con "UseShellExecute" habilitado, pero ese cambio me impide usar nuevas credenciales, por lo que solo puedo hacerlo de a una por vez.

Esto causa algunos problemas: primero, el usuario ya debe existir en la computadora, lo que significa que debe haber iniciado sesión localmente anteriormente. Si no hay un perfil local para ese usuario, la aplicación solicitada a veces se iniciará, pero obtengo excepciones de registro y perfil porque la aplicación espera que existan cosas que todavía no (como una sección de HKCU en el registro, que el usuario no usa). tener porque nunca han iniciado sesión).

Sé que debería ser capaz de simplemente "Elevar" los derechos de mi aplicación para el usuario que están solicitando, iniciar mi nuevo proceso, y luego deshacer la elevación, pero no puedo encontrar una buena muestra de código por eso, y no estoy seguro de que permita correr como un usuario completamente diferente. ¿Tiene sentido? Simplemente no puedo evitar sentir que hay una mejor manera de hacer esto.


ACTUALIZACIÓN: Sólo traté some Impersonation code he encontrado en Internet, pero en vano. Cuando se usa junto con ProcessStartInfo, todavía parece iniciar procesos usando mi inicio de sesión actual, no el que he proporcionado, aunque he activado la suplantación utilizando las credenciales proporcionadas.

Respuesta

3

Lo más probable es que tenga que crear su propia función "shell" con la API de Win32.

Al utilizar la API CreateProcessWithLogonW, puede crear nuevos procesos bajo diferentes credenciales y opcionalmente cargar información de perfil de usuario.

En el siguiente fragmento de código si se reemplaza

  • nombre de usuario - con su nombre de usuario
  • dominio - con su dominio o "vbNullString"
  • contraseña - con su contraseña
  • parámetro 4 - reemplazar 0 con 'INICIAR SESIÓN CON PERFIL' para cargar el perfil de usuarios especificado.

Consulte la documentación del CreateProcessWithLogonW API para obtener más detalles. Al seguir esta ruta, tienes el control total y la total responsabilidad de iniciar la aplicación.

De nuevo, esto es sólo una muestra y es posible que tenga que jugar un poco para que haga lo que quiera.


Imports System.Runtime.InteropServices 

Public Module modShell 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Structure STARTUPINFO 
     Public cb As Integer 
     Public lpReserved As String 
     Public lpDesktop As String 
     Public lpTitle As String 
     Public dwX As Integer 
     Public dwY As Integer 
     Public dwXSize As Integer 
     Public dwYSize As Integer 
     Public dwXCountChars As Integer 
     Public dwYCountChars As Integer 
     Public dwFillAttribute As Integer 
     Public dwFlags As Integer 
     Public wShowWindow As Short 
     Public cbReserved2 As Short 
     Public lpReserved2 As Integer 
     Public hStdInput As Integer 
     Public hStdOutput As Integer 
     Public hStdError As Integer 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Structure PROCESS_INFORMATION 
     Public hProcess As IntPtr 
     Public hThread As IntPtr 
     Public dwProcessId As Integer 
     Public dwThreadId As Integer 
    End Structure 

    Public Declare Unicode Function CreateProcessWithLogonW Lib "Advapi32" (ByVal lpUsername As String, ByVal lpDomain As String, ByVal lpPassword As String, ByVal dwLogonFlags As Int32, ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal dwCreationFlags As Int32, ByVal lpEnvironment As IntPtr, ByVal lpCurrentDirectory As String, ByRef si As STARTUPINFO, ByRef pi As PROCESS_INFORMATION) As Integer 
    Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As Integer 

    Public Const LOGON_WITH_PROFILE As Int32 = &H1 

    Public Const NORMAL_PRIORITY_CLASS As Int32 = &H20& 

    Public Const STARTF_USESHOWWINDOW As Int32 = &H1 
    Public Const SW_HIDE As Int16 = 0 
    Public Const SW_SHOW As Int16 = 5 

    Public Function Shell(ByVal strCmdLine As String, ByVal strCurrentDirectory As String) As Boolean 

     Dim pi As PROCESS_INFORMATION 
     Dim si As New STARTUPINFO 

     si.cb = Marshal.SizeOf(si) 
     si.dwFlags = STARTF_USESHOWWINDOW 
     si.wShowWindow = SW_SHOW 

     Dim result As Integer = CreateProcessWithLogonW("username", "domain", "password", 0, vbNullString, strCmdLine, NORMAL_PRIORITY_CLASS, IntPtr.Zero, strCurrentDirectory, si, pi) 

     If result <> 0 Then 
      Call CloseHandle(pi.hThread) 
      Call CloseHandle(pi.hProcess) 
     Else 
      Return False 
     End If 

     Return True 

    End Function 

End Module 

+0

Voy a intentarlo y ver qué pasa. Si puedo hacerlo sin tener que usar un segundo EXE como un stub, estaré contento, y aún más si no funciona cuando UAC está habilitado (me ocuparé de un aviso). Seré bakc después de que lo haya intentado. – SqlRyan

+0

@SqlRyan y funcionó con UAC habilitado ?? – Jet

1

Puede intentar ejecutar runas desde su aplicación. Algunos ejemplos y opciones here.

0

probar este módulo:

Module Impersonation 

#Region "API Structures" 
    <StructLayout(LayoutKind.Sequential)> _ 
     Public Structure PROCESS_INFORMATION 
     Dim hProcess As System.IntPtr 
     Dim hThread As System.IntPtr 
     Dim dwProcessId As Integer 
     Dim dwThreadId As Integer 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Structure STARTUPINFO 
     Dim cb As Integer 
     Dim lpReserved As System.IntPtr 
     Dim lpDesktop As System.IntPtr 
     Dim lpTitle As System.IntPtr 
     Dim dwX As Integer 
     Dim dwY As Integer 
     Dim dwXSize As Integer 
     Dim dwYSize As Integer 
     Dim dwXCountChars As Integer 
     Dim dwYCountChars As Integer 
     Dim dwFillAttribute As Integer 
     Dim dwFlags As Integer 
     Dim wShowWindow As Short 
     Dim cbReserved2 As Short 
     Dim lpReserved2 As System.IntPtr 
     Dim hStdInput As System.IntPtr 
     Dim hStdOutput As System.IntPtr 
     Dim hStdError As System.IntPtr 
    End Structure 
#End Region 

#Region "API Constants" 
    Private Const LOGON_NETCREDENTIALS_ONLY As Integer = &H2 
    Private Const NORMAL_PRIORITY_CLASS As Integer = &H20 
    Private Const CREATE_DEFAULT_ERROR_MODE As Integer = &H4000000 
    Private Const CREATE_NEW_CONSOLE As Integer = &H10 
    Private Const CREATE_NEW_PROCESS_GROUP As Integer = &H200 
    Private Const LOGON_WITH_PROFILE As Integer = &H1 
#End Region 

#Region "API Functions" 
    Private Declare Unicode Function CreateProcessWithLogon Lib "Advapi32" Alias "CreateProcessWithLogonW" _ 
     (ByVal lpUsername As String, _ 
     ByVal lpDomain As String, _ 
     ByVal lpPassword As String, _ 
     ByVal dwLogonFlags As Integer, _ 
     ByVal lpApplicationName As String, _ 
     ByVal lpCommandLine As String, _ 
     ByVal dwCreationFlags As Integer, _ 
     ByVal lpEnvironment As System.IntPtr, _ 
     ByVal lpCurrentDirectory As System.IntPtr, _ 
     ByRef lpStartupInfo As STARTUPINFO, _ 
     ByRef lpProcessInfo As PROCESS_INFORMATION) As Integer 

    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As System.IntPtr) As Integer 

#End Region 

    Public Sub RunProgram(ByVal UserName As String, ByVal Password As String, ByVal Domain As String, ByVal Application As String, ByVal CommandLine As String) 

     Dim siStartup As STARTUPINFO 
     Dim piProcess As PROCESS_INFORMATION 
     Dim intReturn As Integer 

     If CommandLine Is Nothing OrElse CommandLine = "" Then CommandLine = String.Empty 

     siStartup.cb = Marshal.SizeOf(siStartup) 
     siStartup.dwFlags = 0 

     intReturn = CreateProcessWithLogon(UserName, Domain, Password, LOGON_WITH_PROFILE, Application, CommandLine, _ 
     NORMAL_PRIORITY_CLASS Or CREATE_DEFAULT_ERROR_MODE Or CREATE_NEW_CONSOLE Or CREATE_NEW_PROCESS_GROUP, _ 
     IntPtr.Zero, IntPtr.Zero, siStartup, piProcess) 

     If intReturn = 0 Then 
      Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()) 
     End If 

     CloseHandle(piProcess.hProcess) 
     CloseHandle(piProcess.hThread) 

    End Sub 

End Module 

Uso RunProgram() Para iniciar el programa con el usuario/PW y les gusta. Programm significa que el .exe solamente, los parámetros ar escribe en "línea de comandos"

+0

¿En qué se diferencia este módulo del publicado anteriormente? Honestamente, pregunto: parece bastante similar, así que solo quiero asegurarme de no perderme algo obvio que lo haga claramente superior. Todavía no acepté una respuesta, pero el código anterior ha funcionado para mí hasta ahora. – SqlRyan

+0

sí, es muy similar, es solo una segunda función escrita que redondea la apifunktion CreateProcessWithLogonW API - ready_to_use con errorhandling y successfeedback ... – DrFuture

0

Si desea iniciar una aplicación con unas credenciales distintas a los procesos en ejecución actual, se puede utilizar el .Net Proceso clase.

this.Process = new Process(); 

this.Process.StartInfo.Arguments = "Arguments"; 
this.Process.StartInfo.FileName = "C:\your.exe"; 
this.Process.StartInfo.UserName = "UserName"; 
string password = "some password"; 

this.Process.StartInfo.Password.Clear(); 
foreach (char c in password) 
{ 
    this.Process.StartInfo.Password.AppendChar(c); 
} 


//allow the process to raise events 
this.Process.EnableRaisingEvents = true; 
this.Process.StartInfo.ErrorDialog = false; 
//Method for handling the exit event 
this.Process.Exited += new EventHandler(ApplicationProcess_Exited); 

//Set the application directory as the current working directory 
Environment.CurrentDirectory = System.IO.Directory.GetParent("C:\").ToString(); 

if (this.Process.Start()) 
{ 
    // Do something on start 
} 
Cuestiones relacionadas