Shut Down, Restart, and Log Off From Code

I was recently asked how do you shut the system down from VB.NET, the answer is to a make a few calls to the Win32 API to do this.

First you need to set up a few constants that we will need when we call the Win32 API.

' Constants
Const SE_PRIVILEGE_ENABLED As Integer = &H2
Const TOKEN_QUERY As Integer = &H8
Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege"
' Exit Windows Constants
Const EWX_LOGOFF As Integer = &H0
Const EWX_SHUTDOWN As Integer = &H1
Const EWX_REBOOT As Integer = &H2
Const EWX_FORCE As Integer = &H4
Const EWX_POWEROFF As Integer = &H8
Const EWX_FORCEIFHUNG As Integer = &H10
'Structure
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Friend Structure Luid
   Public Count As Integer
   Public Luid As Long
   Public Attr As Integer
End Structure 'TokPriv1Luid

After we set up our constants we need to define our API calls for the functions we are going to use.

' Get Current Processes.  Click here for the API docs for this function.
<DllImport("kernel32.dll", ExactSpelling:=True)> _
Function GetCurrentProcess() As IntPtr
End Function

' Open Process Token.  Click here for the API docs for this function.
<DllImport("advapi32.dll", SetLastError:=True)> _
Function OpenProcessToken(ByVal h As IntPtr, ByVal acc As Integer, ByRef phtok As IntPtr) As Boolean
End Function

' Look up Priviledge Value.  Click here for the API docs for this function.
<DllImport("advapi32.dll", SetLastError:=True)> _
Friend Function LookupPrivilegeValue(ByVal host As String, ByVal name As String, ByRef pluid As Long) As Boolean
End Function

' Adjust Token Priviledges.  Click here for the API docs for this function.
<DllImport("advapi32.dll", ExactSpelling:=True, SetLastError:=True)> _
Friend Function AdjustTokenPrivileges(ByVal htok As IntPtr, ByVal disall As Boolean, ByRef newst As Luid, ByVal len As Integer, ByVal prev As IntPtr, ByVal relen As IntPtr) As Boolean
End Function

' Exit Windows  Click here for the API docs for this function.
<DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
Friend Function ExitWindowsEx(ByVal flg As Integer, ByVal rea As Integer) As Boolean
End Function

Now that we have done the housekeeping that is needed to make the needed API calls we can write the Shutdown function.

' Exit Windows Sub
Private Sub DoExitWindows(ByVal flg As Integer)
  Dim tp As Luid
  Dim hproc As IntPtr = GetCurrentProcess()
  Dim htok As IntPtr = IntPtr.Zero

  'Get a token for this process. 
  OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, htok)
  tp.Count = 1
  tp.Luid = 0
  tp.Attr = SE_PRIVILEGE_ENABLED

  'Get the LUID for the shutdown privilege.
   LookupPrivilegeValue(Nothing, SE_SHUTDOWN_NAME, tp.Luid)

   'Get the shutdown privilege for this process.
   AdjustTokenPrivileges(htok, False, tp, 0, IntPtr.Zero, IntPtr.Zero)

  'Exit Windows
  ExitWindowsEx(flg, 0)
End Sub

With the function written we can call it with our various constants to determine how it acts.  The three most common methods are listed below.

' Shutdown
DoExitWindows(EWX_SHUTDOWN)

' Restart
DoExitWindows(EWX_REBOOT Or EWX_FORCE)

' Log off
DoExitWindows(EWX_LOGOFF)