RSS
StartseiteKnowledge LibraryTop 10Impressum

10.10 Wie kann ich Windows von VB aus neu starten/beenden?

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.

Links zum Thema