Why ExitWindowsEx won’t Shutdown or Restart the Computer and How to Fix it using Visual Basic.NET and VB 6.0
This question is a very popular one. The ExitWindowsEx question works perfectly fine if using the older versions of Windows like Windows 98 or ME with no special codes to use. You only needed to call the ExitWindowsEx API with the desired parameters and it was good to go. But Kernel changes and such took place with the newer versions of Windows like Windows 2000 and Windows XP. Microsoft now requires that your application have ‘certain privileges’ before it will grant your applications shutdown/restart/logoff request. Visual C++ has these Privileges by default, but not any of the VBs unfortunately. So, the first step you must do is give your application special privileges. The source code below is VB 6.0 code from Microsoft that I converted to Visual Basic .NET. The code here will work with all versions of .NET including 2005 and 2008, plus Visual Basic 2010. There are only minor changes needed to make it work with Visual Basic 5.0/6.0, mainly the variable/parameter types like converting Integers to Long types. IF it didn’t take so much code I would post the .NET and VB 6.0 versions of the code. I went ahead and put the VB.NET source code version.
First setup some structures for the APIs.
Private Structure LUID Dim UsedPart As Integer Dim IgnoredForNowHigh32BitPart As Integer End Structure Private Structure TOKEN_PRIVILEGES Dim PrivilegeCount As Integer Dim TheLuid As LUID Dim Attributes As Integer End Structure
Next you can go ahead and add the API methods to execute the privilege requests.
' 'The API functions below are all used to give the application the proper privilege so the OS will allow the app to Shutdown Windows. ' Private Declare Function OpenProcessToken Lib "advapi32" (ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As Integer) As Integer Private Declare Function LookupPrivilegeValue Lib "advapi32" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, ByRef lpLuid As LUID) As Integer Private Declare Function AdjustTokenPrivileges Lib "advapi32" (ByVal TokenHandle As Integer, ByVal DisableAllPrivileges As Boolean, ByRef NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Integer, ByRef PreviousState As TOKEN_PRIVILEGES, ByRef ReturnLength As Integer) As Integer
Now to setup and create a Sub that will do all the work to set the privileges.
' 'This sub will do all of the work of setting up your apps process using the APIs posted above to get the 'proper privileges to shutdown the OS. 'I originally got this function from msdn and converted it from VB 6.0 to VB.Net and did a tweak here and there. ' Private Sub AdjustToken() Const TOKEN_ADJUST_PRIVILEGES As Int32 = &H20 Const TOKEN_QUERY As Int32 = &H8 Const SE_PRIVILEGE_ENABLED As Int32 = &H2 Dim hdlProcessHandle As IntPtr Dim hdlTokenHandle As Int32 Dim tmpLuid As LUID Dim tkp As TOKEN_PRIVILEGES Dim tkpNewButIgnored As TOKEN_PRIVILEGES Dim lBufferNeeded As Int32 hdlProcessHandle = Process.GetCurrentProcess.Handle OpenProcessToken(hdlProcessHandle, (TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY), hdlTokenHandle) 'Get the LUID for shutdown privilege. LookupPrivilegeValue("", "SeShutdownPrivilege", tmpLuid) tkp.PrivilegeCount = 1 'One privilege to set tkp.TheLuid = tmpLuid tkp.Attributes = SE_PRIVILEGE_ENABLED 'Enable the shutdown privilege in the access token of this process. AdjustTokenPrivileges(hdlTokenHandle, False, tkp, Len(tkpNewButIgnored), tkpNewButIgnored, lBufferNeeded) End Sub
Now that the Privilege codes are done, you just need to call the above Function before the shutdown code. Below is the API call to shutdown/restart/ect.. the computer…
'The function used to actually send the request to shutdown windows. Set the ‘shutdownTypes’ parameter to whether you want windows to “shutdown, reboot, logOff, ect…” You can get those at MSDN or download one of my examples from my VBCodesource.com website. Private Declare Function ExitWindowsEx Lib "user32" (ByVal shutdownType As Integer, ByVal dwReserved As Integer) As Integer
Edit: As per Garry’s post below I decided to go ahead and include the relevent flags when using the ExitWindowsEx function.
Private Const EWX_LOGOFF As Integer = &H0 Private Const EWX_SHUTDOWN As Integer = &H1 Private Const EWX_REBOOT As Integer = &H2 Private Const EWX_FORCE As Integer = &H4 Private Const EWX_POWEROFF As Integer = &H8 Private Const EWX_FORCEIFHUNG As Integer = &H10 '2000/XP only
To shutdown, restart, or logoff the computer you only need to call the AdjustToken() - Sub before the ExitWindowsEx code.
AdjustToken() 'Calls the function to begin executing. ExitWindowsEx(shutdownType, Nothing)
Thats all there is to it! OK, it Is alot of code to get the ExitWindowsEx API to work with Windows 2000/XP/Vista. To bad Microsoft doesn’t give Visual Basic these Privileges by Default like they do with C++. Oh well, don’t have much of a choice when using VB.
Remember, its possible to get these codes to work with Visual Basic 5.0 and 6.0 as well. You need to change the types (Especially Integers to Longs, and Structures to Types) plus process info then it should be ok.
I have pre-made Class Libraries, and Examples for Visual Basic.NET 2002/2003/2005/2008/2010 and I have an Example of doing this in Visual Basic 5.0/6.0 at my Visual Basic Code Source website.
Well thats all for now. Have Fun!