VB4-VB6
Von: Thorsten Dörfler
Unter Windows 95/98/ME war das Herunterfahren von Windows noch eine entspannte Angelegenheit, reicht doch der simple Aufruf von ExitWindowsEx mit den passenden Parametern aus, um Windows zum Abmelden des Benutzers, Herunterfahren des Rechners oder einem Neustart zu veranlassen.
Die Zeiten dieser Systeme sind jedoch so gut wie vorbei und aktuelle Betriebssysteme, wie Windows Vista, XP, aber auch Windows 2000, bauen auf Windows NT auf, wo der Prozess, der Windows herunterfahren möchte, zunächst nach den entsprechenden Privilegien fragen muss, bevor er das Herunterfahren einleiten darf. Dies erfolgt über die API Funktion AdjustTokenPrivileges:
Private Declare Function GetCurrentProcess Lib "kernel32.dll" () As Long Private Type LUID LowPart As Long HighPart As Long End Type Private Type LUID_AND_ATTRIBUTES pLuid As LUID Attributes As Long End Type Private Type TOKEN_PRIVILEGES PrivilegeCount As Long Privileges(1) As LUID_AND_ATTRIBUTES End Type Private Declare Function OpenProcessToken Lib "advapi32.dll" ( _ ByVal ProcessHandle As Long, _ ByVal DesiredAccess As Long, _ ByRef TokenHandle As Long _ ) As Long Private Declare Function CloseHandle Lib "kernel32" ( _ ByVal hObject As Long _ ) As Long Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" _ Alias "LookupPrivilegeValueA" ( _ ByVal lpSystemName As String, _ ByVal lpName As String, _ ByRef lpLuid As LUID _ ) As Long Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" ( _ ByVal TokenHandle As Long, _ ByVal DisableAllPrivileges As Long, _ ByRef NewState As TOKEN_PRIVILEGES, _ ByVal BufferLength As Long, _ ByRef PreviousState As Any, _ ByRef ReturnLength As Long _ ) As Long Private Const TOKEN_ADJUST_PRIVILEGES As Long = &H20 Private Const SE_PRIVILEGE_ENABLED As Long = &H2 Private Declare Function CloseHandle Lib "kernel32" ( _ ByVal hObject As Long _ ) As Long Private Function AdjustPrivilege(ByVal Name As String, _ Optional ByVal Enabled As Boolean = True _ ) As Boolean Dim lToken As Long Dim lProcess As Long Dim lLUID As LUID Dim lPrivileges As TOKEN_PRIVILEGES lProcess = GetCurrentProcess() If CBool(OpenProcessToken(lProcess, TOKEN_ADJUST_PRIVILEGES, lToken)) Then If CBool(LookupPrivilegeValue(vbNullString, Name, _ lPrivileges.Privileges(0).pLuid)) Then lPrivileges.PrivilegeCount = 1 If Enabled Then lPrivileges.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED Else lPrivileges.Privileges(0).Attributes = 0 End If If AdjustTokenPrivileges(lToken, False, lPrivileges, _ Len(lPrivileges), ByVal 0&, 0) Then AdjustShutdownPrivilege = True End If End If CloseHandle lToken End If End Function
Diese Funktion erwartet den Namen des angeforderten Privilegs und gibt bei Erfolg True
zurück. Da Windows 95/98/ME keine Privilegien kennen, brauchen wir noch GetVersionEx, um die aktuelle Windows Version zu ermitteln und die Privilegien nur auf der NT Plattform
anzupassen:
Private Type OSVERSIONINFO dwOSVersionInfoSize As Long dwMajorVersion As Long dwMinorVersion As Long dwBuildNumber As Long dwPlatformId As Long szCSDVersion As String * 128 End Type Private Declare Function GetVersionEx Lib "kernel32.dll" _ Alias "GetVersionExA" ( _ ByRef lpVersionInformation As OSVERSIONINFO _ ) As Long Private Const VER_PLATFORM_WIN32_NT As Long = 2
Nach diesen Vorarbeiten, kann Funktion ShutdownWindows implementiert werden, die die Privilegien anpasst und ExitWindowsEx ausführt.
Private Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege" Private Declare Function ExitWindowsEx Lib "user32" ( _ ByVal uFlags As Long, _ ByVal dwReason As Long _ ) As Long Private Const EWX_LOGOFF As Long = &H0 Private Const EWX_SHUTDOWN As Long = &H1 Private Const EWX_REBOOT As Long = &H2 Private Const EWX_FORCE As Long = &H4 Private Const EWX_POWEROFF As Long = &H8 Private Const EWX_FORCEIFHUNG As Long = &H10 Private Const EWX_RESTARTAPPS As Long = &H40 Private Const SHTDN_REASON_FLAG_PLANNED As Long = &H80000000 Public Enum ShutdownConstants shutdownLogOff = EWX_LOGOFF shutdownNoPowerOff = EWX_SHUTDOWN shutdownReboot = EWX_REBOOT shutdownPowerOff = EWX_POWEROFF End Enum Public Enum ShutdownForceFlags shutdownNone = 0 shutdownForce = EWX_FORCE shutdownForceIfHung = EWX_FORCEIFHUNG End Enum Public Sub ShutdownWindows(ByVal Mode As ShutdownConstants, _ Optional ByVal Force As ShutdownForceFlags = shutdownNone, _ Optional ByVal RestartApps As Boolean = False) Dim lOSV As OSVERSIONINFO lOSV.dwOSVersionInfoSize = Len(lOSV) GetVersionEx lOSV If lOSV.dwPlatformId = VER_PLATFORM_WIN32_NT Then AdjustPrivilege SE_SHUTDOWN_NAME, True End If Mode = Mode Or Force If RestartApps And lOSV.dwMajorVersion >= 6 Then Mode = Mode Or EWX_RESTARTAPPS End If ExitWindowsEx Mode, SHTDN_REASON_FLAG_PLANNED End Sub
Um Windows neuzustarten reicht der Aufruf:
ShutdownWindows shutdownReboot, shutdownNone
Zum Abschalten des Rechners:
ShutdownWindows shutdownPowerOff, shutdownNone
Der Parameter Force
sollte nur in Ausnahmefällen eingesetzt werden, da ansonsten laufenden Anwendungen keine Gelegenheit gegeben wird ihre Einstellungen zu speichern.
Beachten Sie, dass diese Methode als Grund für das Herunterfahren immer einen geplanten Vorgang annimt SHTDN_REASON_FLAG_PLANNED
, da davon auszugehen ist, dass diese Methode nicht just for fun verwendet wird, sondern nur dann, wenn es wirklich erforderlich ist. Der Grund für das Herunterfahren wird jedoch erst von Windows XP/2003/Vista berücksichtigt. Vorherige Versionen ignorieren diesen Parameter.
Der Parameter RestartApps
ist erst unter Windows Vista von Bedeutung. Aktuell laufende Anwendungen haben hier die Möglichkeit sich für einen automatischen Start zu registrieren, wenn Windows wieder gestartet wurde. Dazu sendet es eine spezielle Fensternachricht an die laufenden Anwendungen.