|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||
| Visual Basic System Services InitiateSystemShutdown: Terminating Remote Windows Sessions |
||
| Posted: | Wednesday December 08, 2004 | |
| Updated: | Monday December 26, 2011 | |
| Applies to: | VB4-32, VB5, VB6 | |
| Developed with: | VB6, Windows XP | |
| OS restrictions: | Windows NT4, Windows 2000, Windows XP, Windows 2003 | |
| Author: | VBnet - Randy Birch | |
|
Related: |
ExitWindowsEx: Shut Down, Reboot, Log Off or Power Off | |
| Prerequisites |
| None. |
|
|
Windows'
InitiateSystemShutdown function initiates a shutdown and optional
restart of the specified computer. Although the user of the remote
machine targeted will receive a message box-like notification of the
pending shutdown, the user can not cancel the shutdown from occurring.
As a minimum, the name of the remote machine must be specified, and it can be in the format \\machinename, machinename alone, or the IP address without leading slashes. The lpMessage parameter allows you to send the user of the remote computer a message indicating why you are shutting down the machine. The dwTimeout parameter indicates the countdown that the dialog will perform. A reasonable time should be provided to allow the user to save open files, as the shutdown is not delayed while apps are open or a Save As dialog is on-screen. Specifying 0 as dwTimeout causes the shutdown to occur immediately without any warning to the user.
The bRebootAfterShutdown flag, when non-zero, causes the system to reboot. Once a shutdown has been started using InitiateSystemShutdown, its sister API, AbortSystemShutdown, can cancel the action. AbortSystemShutdown requires just the name of the remote machine. The demo presented enumerates all machines on the domain or workgroup to populate the combo box. Controls are disabled until a combo selection has been made. As a confirmation, on selection the "Shut down selected machine" check box is enabled to confirm the shutdown action is desired. Checking this box enables the Force, Reboot and Delay options, and the Perform command button. Because you'll probably want to only ensure the functions execute properly, the a successful call will disable the Perform button and enable the Abort button. The machine name combo is also disabled to prevent changing the machine name in case you want to hit Abort. In a real utility this extra code would probably not be needed, as you'd probably want to execute the command to several machines in a loop. One thing I noticed when bRebootAfterShutdown was False is that instead of the remote computer powering off, it went to its "It is now safe to turn off your computer" screen. (I didn't know XP still had this screen!) This may be just an anomaly with my setup; sure would have been nice to have the machine actually power off. When bRebootAfterShutdown is non-zero the machine shuts right down then restarts Windows. InitiateSystemShutdown can not be used to shut down the machine executing the call (the local machine) -- for this ExitWindowsEx must be used. In addition, InitiateSystemShutdown does not cause an entry to be added to the remote machine's event log. If event recording is required, use InitiateSystemShutdownEx which accommodates a DWORD value to indicate the reason for the shutdown. |
| BAS Module Code |
| None. |
|
|
| Form Code |
|
|
|
To a form, add a combo (Combo1) for the machine names, and a second combo
(Combo2) for the delay duration. Also add three check boxes (Check1,
Check2, Check3), a multi-line text box (Text1), and a label (Label1).
The code below will fill in the control captions. Add two command buttons (Command1, Command2) along with 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. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Windows type used to call the Net API
Private Const MAX_PREFERRED_LENGTH As Long = -1
Private Const NERR_SUCCESS As Long = 0&
Private Const ERROR_MORE_DATA As Long = 234&
Private Const SV_TYPE_ALL As Long = &HFFFFFFFF
Private Const MAX_COMPUTERNAME As Long = 16
Private Type SERVER_INFO_100
sv100_platform_id As Long
sv100_name As Long
End Type
Private Declare Function NetServerEnum Lib "Netapi32" _
(ByVal servername As Long, _
ByVal level As Long, _
buf As Any, _
ByVal prefmaxlen As Long, _
entriesread As Long, _
totalentries As Long, _
ByVal servertype As Long, _
ByVal domain As Long, _
resume_handle As Long) As Long
Private Declare Function NetApiBufferFree Lib "netapi32.dll" _
(ByVal Buffer As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(pTo As Any, uFrom As Any, _
ByVal lSize As Long)
Private Declare Function lstrlenW Lib "kernel32" _
(ByVal lpString As Long) As Long
Private Declare Function InitiateSystemShutdown Lib "advapi32.dll" _
Alias "InitiateSystemShutdownA" _
(ByVal lpMachineName As String, _
ByVal lpMessage As String, _
ByVal dwTimeout As Long, _
ByVal bForceAppsClosed As Long, _
ByVal bRebootAfterShutdown As Long) As Long
Private Declare Function AbortSystemShutdown Lib "advapi32.dll" _
Alias "AbortSystemShutdownA" _
(ByVal lpMachineName As String) As Long
Private Declare Function GetComputerName Lib "kernel32" _
Alias "GetComputerNameA" _
(ByVal lpBuffer As String, _
nSize As Long) As Long
Private Sub Form_Load()
Dim sLocalMachine As String
sLocalMachine = GetLocalComputerName()
Call GetServers(sLocalMachine, Combo1)
With Combo1
.ListIndex = 0
.ListIndex = -1
End With
With Combo2
.AddItem "(immediate)"
.AddItem "5"
.AddItem "10"
.AddItem "15"
.AddItem "20"
.AddItem "30"
.AddItem "45"
.AddItem "60"
.ListIndex = -1
End With
Text1.Text = "The VBnet InitiateSystemShutdown Demo is " & _
"closing the current session. Kiss your apps goodbye!"
Command1.Caption = "Perform Shutdown"
Command2.Caption = "Abort Shutdown"
Check1.Caption = "Shut down selected machine"
Check2.Caption = "Force open apps closed if needed"
Check3.Caption = "Reboot the computer"
Label1.Caption = "Delay shutdown for seconds"
'invoke the check1 click event to set
'the initial control states
Check1.Value = vbChecked
Check1.Value = vbUnchecked
End Sub
Private Sub Command1_Click()
Dim sMachine As String
Dim sAlertMessage As String
Dim dwDelay As Long
Dim dwForce As Long
Dim dwReboot As Long
Dim dwSuccess As Long
'set up the parameters
sMachine = "\\" & Combo1.List(Combo1.ListIndex)
'alternate formats:
'sMachine = "192.168.1.101"
'sMachine = Combo1.List(Combo1.ListIndex)
sAlertMessage = Text1.Text & vbNullChar
dwForce = Abs(Check2.Value = vbChecked)
dwReboot = Abs(Check3.Value = vbChecked)
'cause you're bound to forget!
If Combo2.ListIndex > -1 Then
dwDelay = Val(Combo2.List(Combo2.ListIndex))
Else
dwDelay = 30
End If
'success will be non-zero if successful.
'Err.LastDllError will return the error
'code if a problem, eg 5 - access denied.
dwSuccess = InitiateSystemShutdown(sMachine, sAlertMessage, dwDelay, dwForce, dwReboot)
'prevent changing the machine name in case
'an abort is desired, and enable the abort button
Combo1.Enabled = dwSuccess = 0
Command1.Enabled = dwSuccess = 0
Command2.Enabled = dwSuccess <> 0
End Sub
Private Sub Command2_Click()
Dim sMachine As String
sMachine = "\\" & Combo1.List(Combo1.ListIndex)
AbortSystemShutdown sMachine
Combo1.Enabled = True
Command1.Enabled = True
Command2.Enabled = False
End Sub
Private Sub Check1_Click()
Check2.Enabled = Check1.Value = vbChecked
Check3.Enabled = Check1.Value = vbChecked
Combo2.Enabled = Check1.Value = vbChecked
Label1.Enabled = Check1.Value = vbChecked
Command1.Enabled = Check1.Value = vbChecked
Command2.Enabled = False
End Sub
Private Sub Combo1_Click()
Check1.Enabled = Combo1.ListIndex <> -1
End Sub
Private Function GetServers(sLocalMachine As String, ctl As Control) As Long
'list all machines of the specified type
'that are visible in the domain/workgroup
Dim bufptr As Long
Dim dwEntriesread As Long
Dim dwTotalentries As Long
Dim dwResumehandle As Long
Dim se100 As SERVER_INFO_100
Dim success As Long
Dim nStructSize As Long
Dim cnt As Long
Dim tmp As String
nStructSize = LenB(se100)
'Call passing MAX_PREFERRED_LENGTH to have the
'API allocate required memory for the return values.
'The MSDN states servername must be NULL (0&).
success = NetServerEnum(0&, _
100, _
bufptr, _
MAX_PREFERRED_LENGTH, _
dwEntriesread, _
dwTotalentries, _
SV_TYPE_ALL, _
0&, _
dwResumehandle)
'if all goes well
If success = NERR_SUCCESS And _
success <> ERROR_MORE_DATA Then
'loop through the returned data, adding
'each machine to the list
For cnt = 0 To dwEntriesread - 1
'get one chunk of data and cast
'into an LOCALGROUP_INFO_1 type
'in order to add the name to a list
CopyMemory se100, ByVal bufptr + (nStructSize * cnt), nStructSize
'if the machine is the local machine, don't bother
'adding it as you can't shut down your own machine
'using InitiateSystemShutdown
tmp = LCase$(GetPointerToByteStringW(se100.sv100_name))
If tmp <> sLocalMachine Then
ctl.AddItem tmp
End If
Next
End If
'clean up, regardless of success
Call NetApiBufferFree(bufptr)
End Function
Private Function GetLocalComputerName() As String
Dim tmp As String
'return the name of the computer
tmp = Space$(MAX_COMPUTERNAME)
If GetComputerName(tmp, Len(tmp)) <> 0 Then
GetLocalComputerName = LCase$(TrimNull(tmp))
End If
End Function
Private Function GetPointerToByteStringW(ByVal dwData As Long) As String
Dim tmp() As Byte
Dim tmplen As Long
If dwData <> 0 Then
tmplen = lstrlenW(dwData) * 2
If tmplen <> 0 Then
ReDim tmp(0 To (tmplen - 1)) As Byte
CopyMemory tmp(0), ByVal dwData, tmplen
GetPointerToByteStringW = tmp
End If
End If
End Function
Private Function TrimNull(startstr As String) As String
TrimNull = Left$(startstr, lstrlenW(StrPtr(startstr)))
End Function
|
| Comments |
|
From the MSDN: To shut down the local computer, the calling thread must have the SE_SHUTDOWN_NAME privilege. To shut down a remote computer, the calling thread must have the SE_REMOTE_SHUTDOWN_NAME privilege on the remote computer. By default, users can enable the SE_SHUTDOWN_NAME privilege on the computer they are logged onto, and administrators can enable the SE_REMOTE_SHUTDOWN_NAME privilege on remote computers. For more information, see Running with Special Privileges. Common reasons for failure include an invalid or inaccessible computer name or insufficient privilege. The error ERROR_SHUTDOWN_IN_PROGRESS is returned if a shutdown is already in progress on the specified computer. A non-zero return value does not mean the logoff was or will be successful. The shutdown is an asynchronous process, and it can occur long after the API call has returned, or not at all. Even if the timeout value is zero, the shutdown can still be aborted by applications, services, or even the system. The non-zero return value indicates that the validation of the rights and parameters was successful and that the system accepted the shutdown request. When this function is called, the caller must specify whether or not applications with unsaved changes should be forcibly closed. If the caller chooses not to force these applications to close and an application with unsaved changes is running on the console session, the shutdown will remain in progress until the user logged into the console session aborts the shutdown, saves changes, closes the application, or forces the application to close. During this period the shutdown may not be aborted except by the console user, and another shutdown may not be initiated. Note that calling this function with the value of the bForceAppsClosed parameter set to TRUE avoids this situation. Remember that doing this may result in loss of data. Windows Server 2003, Windows XP: If the computer is locked and the bForceAppsClosed parameter is FALSE, the last error code is ERROR_MACHINE_LOCKED. If the system is not ready to handle the request, the last error code is ERROR_NOT_READY. The application should wait a short while and retry the call. |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |