|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Two flags allow you to perform special actions. Although the API returns a success flag indicating the shutdown has been initiated on the remote machine, there is no message returned to indicate that the shutdown of the remote machine is going to be successful. Therefore when the bForceAppsClosed flag is non-zero Windows shuts down even when encountering hung apps. 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. |