Visual Basic System Services
ExitWindowsEx: Shut Down, Reboot, Log Off or Power Off
     
Posted:   Thursday August 22, 2002
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   None
Author:   VBnet - Randy Birch
     

Related:  

InitiateSystemShutdown: Terminating Remote Windows Sessions
     
 Prerequisites
None.

ExitWindowsEx provides the ability for an application to log off the current user, shut down the system, shut down and restart Windows, or shut down and power off the machine (where supported by the system).

This demo shows how to call this API, how to set the appropriate flags, and how to call AdjustTokenPrivileges in order to allow the current process sufficient rights to execute ExitWindowsEx under NT/2000/XP. Win9x systems do not call AdjustTokenPrivileges, so to use the code on a 9x box you can just add an Else condition to the Command1_Click code and call the API directly, or create another wrapper allowing you to specify the flags etc.

ExitWindowsEx can be unforgiving. You may have open apps with unsaved changes that would normally prompt for file saving, and where normally pressing the Save dialog's Cancel button would terminate the shutdown. In performing the shutdown, Windows chooses the shutdown "order" of applications and services therefore the exact shutdown order can not be predicted. This can wreck havoc.

To test parts of this code I didn't want to actually shutdown every time I pressed the button, so I created a Notepad doc with unsaved text that would cause the "want to save?" prompt to appear on closing, I could then hit cancel to cancel the shutdown** and this worked as expected. Sort of.

Because Windows determines the shutdown order of applications and services, calling this API may result in the shutdown of services before the test notepad app presents the 'want to save?' prompt. More often than not my mouse driver was nuked since its shutdown occurred before the prompt for notepad, and therefore required me to perform a shutdown (reboot) after all. Don't call this API unless you really, really intend to shut down the system. And only call it after all cleanup within your own app has been performed. 

** Should either FORCE flag be specified, you are indicating you want nothing to prevent the shutdown/logoff/reboot action from taking place. And trust me this is what happens. For example., although my dirty Notepad app still presented its 'want to save?' dialog, canceling the dialog when a FORCE flag was specified now did nothing more than exercise my finger -- I could no longer abort the action. So be sure you can afford to loose anything currently open and unsaved. Including this demo!

 BAS Module Code
None:

 Form Code
Add a command button (Command1) and four option buttons (Option1 - Option4), as well as two check boxed (Check1, Check2), and the following code:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Const TOKEN_ADJUST_PRIVILEGES As Long = &H20
Private Const TOKEN_QUERY As Long = &H8
Private Const SE_PRIVILEGE_ENABLED As Long = &H2

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 '2000/XP only

Private Const VER_PLATFORM_WIN32_NT As Long = 2

Private Type OSVERSIONINFO
  OSVSize         As Long
  dwVerMajor      As Long
  dwVerMinor      As Long
  dwBuildNumber   As Long
  PlatformID      As Long
  szCSDVersion    As String * 128
End Type

Private Type LUID
   dwLowPart As Long
   dwHighPart As Long
End Type

Private Type LUID_AND_ATTRIBUTES
   udtLUID As LUID
   dwAttributes As Long
End Type

Private Type TOKEN_PRIVILEGES
   PrivilegeCount As Long
   laa As LUID_AND_ATTRIBUTES
End Type
      
Private Declare Function ExitWindowsEx Lib "user32" _
   (ByVal dwOptions As Long, _
   ByVal dwReserved As Long) As Long

Private Declare Function GetCurrentProcess Lib "kernel32" () As Long

Private Declare Function OpenProcessToken Lib "advapi32" _
  (ByVal ProcessHandle As Long, _
   ByVal DesiredAccess As Long, _
   TokenHandle As Long) As Long

Private Declare Function LookupPrivilegeValue Lib "advapi32" _
   Alias "LookupPrivilegeValueA" _
  (ByVal lpSystemName As String, _
   ByVal lpName As String, _
   lpLuid As LUID) As Long

Private Declare Function AdjustTokenPrivileges Lib "advapi32" _
  (ByVal TokenHandle As Long, _
   ByVal DisableAllPrivileges As Long, _
   NewState As TOKEN_PRIVILEGES, _
   ByVal BufferLength As Long, _
   PreviousState As Any, _
   ReturnLength As Long) As Long

Private Declare Function GetVersionEx Lib "kernel32" _
   Alias "GetVersionExA" _
  (lpVersionInformation As OSVERSIONINFO) As Long
  
  
  
Private Sub Command1_Click()

   Dim uflags As Long
   Dim success As Long
   
   If Option1.Value = True Then uflags = EWX_LOGOFF
   If Option2.Value = True Then uflags = EWX_SHUTDOWN
   If Option3.Value = True Then uflags = EWX_REBOOT
   If Option4.Value = True Then uflags = EWX_POWEROFF
   
   If Check1.Value = vbChecked Then uflags = uflags Or EWX_FORCE
   If Check2.Value = vbChecked Then uflags = uflags Or EWX_FORCEIFHUNG
     
  'if running under NT or better,
  'the shutdown privileges need to
  'be adjusted to allow the ExitWindowsEx
  'call. If the adjust call fails on a NT+
  'system, success holds False, preventing shutdown.
   If IsWinNTPlus() Then
   
      success = EnableShutdownPrivledges()
      If success Then Call ExitWindowsEx(uflags, 0&)
         
   Else
   
     '9x system, so just do it 
      Call ExitWindowsEx(uflags, 0&)
      
   End If

End Sub
  

Private Function IsWinNTPlus() As Boolean

  'returns True if running Windows NT,
  'Windows 2000, Windows XP, or .net server
   #If Win32 Then
  
      Dim OSV As OSVERSIONINFO
   
      OSV.OSVSize = Len(OSV)
   
      If GetVersionEx(OSV) = 1 Then

         IsWinNTPlus = (OSV.PlatformID = VER_PLATFORM_WIN32_NT) And _
                       (OSV.dwVerMajor >= 4)
      End If

   #End If

End Function


Private Function EnableShutdownPrivledges() As Boolean

   Dim hProcessHandle As Long
   Dim hTokenHandle As Long
   Dim lpv_la As LUID
   Dim token As TOKEN_PRIVILEGES
   
   hProcessHandle = GetCurrentProcess()
   
   If hProcessHandle <> 0 Then
   
     'open the access token associated
     'with the current process. hTokenHandle
     'returns a handle identifying the
     'newly-opened access token
      If OpenProcessToken(hProcessHandle, _
                        (TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY), _
                         hTokenHandle) <> 0 Then
   
        'obtain the locally unique identifier
        '(LUID) used on the specified system
        'to locally represent the specified
        'privilege name. Passing vbNullString
        'causes the api to attempt to find
        'the privilege name on the local system.
         If LookupPrivilegeValue(vbNullString, _
                                 "SeShutdownPrivilege", _
                                 lpv_la) <> 0 Then
         
           'TOKEN_PRIVILEGES contains info about
           'a set of privileges for an access token.
           'Prepare the TOKEN_PRIVILEGES structure
           'by enabling one privilege.
            With token
               .PrivilegeCount = 1
               .laa.udtLUID = lpv_la
               .laa.dwAttributes = SE_PRIVILEGE_ENABLED
            End With
   
           'Enable the shutdown privilege in
           'the access token of this process.
           'hTokenHandle: access token containing the
           '  privileges to be modified
           'DisableAllPrivileges: if True the function
           '  disables all privileges and ignores the
           '  NewState parameter. If FALSE, the
           '  function modifies privileges based on
           '  the information pointed to by NewState.
           'token: TOKEN_PRIVILEGES structure specifying
           '  an array of privileges and their attributes.
           '
           'Since were just adjusting to shut down,
           'BufferLength, PreviousState and ReturnLength
           'can be passed as null.
            If AdjustTokenPrivileges(hTokenHandle, _
                                     False, _
                                     token, _
                                     ByVal 0&, _
                                     ByVal 0&, _
                                     ByVal 0&) <> 0 Then
                                     
              'success, so return True
               EnableShutdownPrivledges = True
   
            End If  'AdjustTokenPrivileges
         End If  'LookupPrivilegeValue
      End If  'OpenProcessToken
   End If  'hProcessHandle

End Function
 Comments
Given the caveats in the description above, be sure to save the project (and any other critical files) before running!

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter