Εχω το εξής θέμα:
Χρειάζεται να καλέσω μια εφαρμογή (για την ακρίβεια, ΙΕ με ένα συγκεκριμένο URL) ως ένας συγκεκριμένος χρήστης (και όχι προφανώς αυτός που τρέχει την αρχική εφαρμογή) μέσα από μια άλλη εφαρμογή.
Χρησιμοποιώ .NET 1.1 οπότε η System.Diagnostics.Process.CreateProcess δεν με καλύπτει μια και δεν μπορεί να δεχθεί user credentials (σε αντίθεση με το .NET 2.0).
Αρα η μόνη λύση στην οποία μπορούσα να καταφύγω ήταν API Call. Δεν έχω μεγάλη γνώση από αυτόν τον τομέα, με πολύ κόπο κατάφερα να βρω κώδικα σε C# τον οποίο άλλαξα σε VB.NET και ενσωματωσα σε μια δική μου κλάση και δουλεύει. Χρησιμοποιεί το advapi32.dll και συγκεκριμένα την CreateProcessWithLogonW(). Θα σας παραθέσω τον κώδικα παρακάτω μια και είναι χρήσιμος άν κάποιος άλλος συνάδελφος θέλει να κάνει το ίδιο πράγμα.
Το πρόβλημα
Η CreateProcessWithLogonW δουλεύει μόνο σε Win2000/2003. Ο πελάτης (δυστυχώς) έχει και παλιότερα συστήματα όπως NT 4.0 Workstation. Αυτό είναι κάτι που δεν μπορώ να αλλάξω.
Ψάχνοντας, βρήκα οτι η CreateProcessAsUser() γενικά παίζει και σε αυτά τα συστήματα. Δυστυχώς, λόγω του οτι λειτουργεί με tokens και όχι με actual username/password/domain, δεν είναι τόσο απλή η υλοποίησή της και στο Web όλος ο κόσμος τσακώνεται για το πως θα λειτουργήσει σωστά...
Το ζητούμενο
Χρειάζομαι ολοκληρωμένο κώδικα ο οποίος θα λειτουργεί ΚΑΙ σε NT 4.0 Workstation και ο οποίος θα σηκώνει μια εξωτερική εφαρμογή ως χρήστης της επιλογής μου. Δεν με ενοχλεί να περνάει το password ή οχι, ως θέμα security, σε αυτό το σημείο. Ξερω οτι είναι αρκετά "βαρύ" το θέμα, οπότε απλα εύχομαι κάποιος φίλος να έχει ήδη "περάσει" αυτή τη φουρτούνα και να έχει κάτι να προτείνει.
Παρακάτω παραθέτω τον κώδικα που χρησιμοποιεί την CreateProcessWithLogonW και ο οποίος δούλεψε μια χαρά σε Windows 2003 server (αλλα φυσικά όχι σε ΝΤ):
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Imports System.Runtime.InteropServices
Imports System.IO
Public Class clsSpawnProcess
<Flags()> _
Enum LogonFlags
LOGON_WITH_PROFILE = &H1
LOGON_NETCREDENTIALS_ONLY = &H2
End Enum
<Flags()> _
Enum CreationFlags
CREATE_SUSPENDED = &H4
CREATE_NEW_CONSOLE = &H10
CREATE_NEW_PROCESS_GROUP = &H200
CREATE_UNICODE_ENVIRONMENT = &H400
CREATE_SEPARATE_WOW_VDM = &H800
CREATE_DEFAULT_ERROR_MODE = &H4000000
End Enum
<StructLayout(LayoutKind.Sequential)> _
Structure ProcessInfo
Public hProcess As IntPtr
Public hThread As IntPtr
Public dwProcessId As UInt32
Public dwThreadId As UInt32
End Structure
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Structure StartupInfo
Public cb As Int32
Public reserved1 As String
Public desktop As String
Public title As String
Public dwX As UInt32
Public dwY As UInt32
Public dwXSize As UInt32
Public dwYSize As UInt32
Public dwXCountChars As UInt32
Public dwYCountChars As UInt32
Public dwFillAttribute As UInt32
Public dwFlags As UInt32
Public wShowWindow As Short
Public reserved2 As Short
Public reserved3 As Integer
Public hStdInput As IntPtr
Public hStdOutput As IntPtr
Public hStdError As IntPtr
End Structure
<DllImport("advapi32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function CreateProcessWithLogonW( _
ByVal principal As String, _
ByVal authority As String, _
ByVal password As String, _
ByVal logonFlags As LogonFlags, _
ByVal appName As String, _
ByVal cmdLine As String, _
ByVal creationFlags As CreationFlags, _
ByVal environmentBlock As IntPtr, _
ByVal currentDirectory As String, _
ByRef startupInfo As StartupInfo, _
ByRef processInfo As ProcessInfo) As Boolean
End Function
<DllImport("kernel32.dll")> _
Public Shared Function CloseHandle(ByVal h As IntPtr) As Boolean
End Function
Public Shared Function CreateProcess( _
ByVal username As String _
, ByVal domain As String _
, ByVal password As String _
, ByVal applicationPath As String _
, ByVal commandLineArgs As String)
Dim si As StartupInfo = New StartupInfo
si.cb = Marshal.SizeOf(GetType(StartupInfo))
Dim pi As ProcessInfo = New ProcessInfo
If (CreateProcessWithLogonW(username, domain, password, _
LogonFlags.LOGON_WITH_PROFILE, _
applicationPath, commandLineArgs, _
0, IntPtr.Zero, Nothing, _
si, pi)) Then
CloseHandle(pi.hProcess)
CloseHandle(pi.hThread)
Else
'TODO: throw exception here or something
Console.WriteLine("Error code: {0}", Marshal.GetLastWin32Error())
End If
End Function
End Class
Τα φώτα σας!
Σωτήρης Φιλιππίδης
DotSee Web Services