Remote ShutDown

skea

Registered User.
Local time
Today, 22:20
Joined
Dec 21, 2004
Messages
342
I got this code from some where to Shutdown a Network Machine Remotely.
Amazingly, its not working and I dont know whats wrong with it. has someone done this before!
I would like to know whats wrong with it.


Code:
Option Compare Database
Option Explicit
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)
   
    
   
  'invoke the check1 click event to set
  'the initial control states
    Me.Check1 = True
    Me.Check1 = True
   
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 = Me.Combo1
  'alternate formats:
  'sMachine = "192.168.1.101"
  'sMachine = Combo1.List(Combo1.ListIndex)
  
   Me.Text1.SetFocus
   sAlertMessage = Me.Text1
   dwForce = Abs(Check2 = True)
   dwReboot = Abs(Check3 = True)
   
  'cause you're bound to forget!
   If Me.Combo2.Value > -1 Then
      dwDelay = Val(Me.Combo2)
   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
    Me.Combo1.Enabled = dwSuccess = 0
    Me.Command1.Enabled = dwSuccess = 0
    Me.Command2.Enabled = dwSuccess <> 0
   
End Sub
Private Sub Command2_Click()

   Dim sMachine As String
   
   sMachine = Me.Combo1
   AbortSystemShutdown sMachine
   
   Me.Combo1.Enabled = True
   Me.Command1.Enabled = True
   Me.Command2.Enabled = False
   
End Sub
Private Sub Check1_Click()

    Me.Check2.Enabled = True
    Me.Check3.Enabled = True
    Me.Command1.Enabled = True
    Me.Command2.Enabled = False
   
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
 
Last edited:
I'm going to start by tossing the ball back into your court.

What's wrong with it?

(Read the sticky "how to ask questions" at the top of this forum. Pay attention to the part that talks about describing SYMPTOMS...)

BTW, I can guess why you might want to do this but I believe it requires certain rights on the remote machine. If this is in a true domain environment, you have to at least be a member of an Operator group to do a machine shutdown unless you are the "owner" of the machine. And depending on domain policy, the best you might be able to force would be a restart, not a true shutdown.

Now, you could always search this forum for other solutions to getting users to log out of shared databases. Yours is certainly one of the more aggressive methods I've seen. I wouldn't try this very often, though.

Like, if I shut down my boss's machine fifteen seconds before he hit the "SAVE" button on this year's salary adjustment recommendations, I'm sure he would happier than a dyspeptic dragon. (Why? Because he has found a new place to crap... my desk.)
 
The_Doc_Man said:
I'm going to start by tossing the ball back into your court.
What's wrong with it?

Thanks Doc-The-Man,
the code works where i got it from, but it doesnt for me. I cant even send my packet message to a remote machine.Where i am, i have exclusive and explicit rights to all the Machines but i need this because some people forget shuting down the machines when they are going off work yet they lock their offices, so i thought this will be a tool for me . I know i can do this through any other remotedown software, or even the normal(managePC-Actions-Access anotherPC- ShutDown/Reboot/etc) but i want it through VBA code. thats why when i could not think any further i posted the code on the Forum.(I had already read the Sticky You directed me To).

Any how, any suggestion is appreciated.
 
Last edited:
I cant even send my packet message to a remote machine.

Why can you not send a packet message? That is not intended as a flip answer. It is the crux of your problem. I'm a little bit pedantic when it comes to problem analysis. Or had you noticed? ;)

Are we talking ... Any remote machine? Some remote machines? One particular remote machine?

HINT: On your own machine, look at Start >> Settings >> Control Panel >> Services. This depends on Windows version so might take a different path to reach it. Also, depending on version, "Services" might be subsumed into the "Management" tools or "Administrative" tools section on your system. In any case, find your Windows "Services" working list.

See if Windows Messaging Service is disabled or stopped. See if Remote Procedure Call Service is disabled or stopped. Local Proc.Call is probably running 'cause you can't run Windows without that. But RPC is not ALWAYS required. You cannot send a packet anywhere externally without these things running. The same services also have to be running on the other machines, too, or you are SOL and S doesn't mean "strictly" in this case.

You mentioned a method of forcing another machine to shut down through Windows administrative utilities. If this works and your VBA does not, you obviously are not calling the right subroutines. Have you searched the MS Knowledge Base for what you are trying to do?

VBA can do a lot, and if you have access to the right system library calls, you can almost anything. However, don't forget that the library routines are based on C++ as the callling language (hence the need for the funky external declaration and awkward record definitions). C++ also supports address variables (pointers). If you ever mismanage even ONE address-oriented argument, you are screwed.

I repeat, what are your SYMPTOMS. Like, error message? Error trap? Sitting and staring back at you like a blind glass eye? Jeez, give us SOME sort of feedback here. Saying you can't send a packet is one isolated symptom out of many. (Or if it is the ONLY symptom you got, you aren't trapping errors adequately.)

Debugging at this level includes the fine art of INSTRUMENTATION. I.e. building in an error reporting tool to trap errors when they occur. OnError statements are your FRIENDS. Just use them now and then when debugging potentially complex or "touchy" code.
 
Just a thought...

Didn't the Windows XP SP2 update turn off most remote PC functions as a blanket security "fix".
 
ghudson - you are right, but it is not clear exactly which ones they turned on and off, which is why I suggested looking at the services list.

But if his other method works then the services list is not the problem. It is just that skea has been rather ... sparse... with his problem description.
 
If the purpose is to do some updates/development on an Access database, you have another option. I'll just give you the outlines here first.

When the users start the db, open a connection to where the back end is located. I use a form that's hidden and is always open when my app is running. In each FE running, I have some code that checks to see if a checkbox is 'True' or 'False'. I've called the checkbox 'KickOut'.
The check box is bound to a linked table located on the backend.

Now I can kick everyone out of my app by setting the 'KickOut'-checkbox to 'True'. This because the hidden form runs on a timer event that checks the state of the checkbox. If it's True, the procedure does a Applicatopn.Quit.

This works great for my use, and gets me in control of the db. I have built this so a 'message box' (read form) pops up at first, telling that the db will be shut down after 5 minutes due to maintenance.

If anyone tryes to re-start the db, the db checks the 'KickOut' box, and if set to True, I open a PowerPoint slide, informing them about the maintenance.

So if the purpose is to do a System Shutdown, this don't work. However - if it's database maintenance it does.
 
Got Off The Forum and Got Things Working!

Folks thanks, for all your suggestions.

My problem was clear, i never wanted a remote database shutdown but a remote system shutdown. I decided to go off this forum and have a 2hrs thinking. A combination of ideas from MS Knowlwdge base and VBnet + one line of ghudson help code got every thing running. Now My Code can Shutdown,Reboot,Standby a remote PC.
I can schedule the app to run in a hidden state,then it fires up a countdown form without an application background,renames a certain file to do the shutdown,reboot,standby etc...

When You get a BMW which you cant move an inch and there seems no one to help, you rather drop it and try other ways than wasting time. So i dropped that code i posted earlier, i think i got it from vbnet.

Thanks Again.
 
skea,

Can you post the code routine(s) you used for your remote system shutdown? That would help those who responded to this thread and also any future readers of this thread.

Thanks!
 
ghudson said:
skea,

Can you post the code routine(s) you used for your remote system shutdown? That would help those who responded to this thread and also any future readers of this thread.

Thanks!

Here it is:
What i do is create a file and an application with two forms(the Hidden Form, and a Message form)on a client PC. I also create another application on the server which can rename the file on the client at a certain time.
When the file is renamed, the hidden form loads the Message form which has a countdown timer.
This Code is for the client application(form that calls the form frmMessage)
Code:
Option Compare Database
Option Explicit
Dim boolCountDown As Boolean
Dim intCountDownSeconds As Integer
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 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
      If OpenProcessToken(hProcessHandle, _
                        (TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY), _
                         hTokenHandle) <> 0 Then
   
         If LookupPrivilegeValue(vbNullString, _
                                 "SeShutdownPrivilege", _
                                 lpv_la) <> 0 Then
            With token
               .PrivilegeCount = 1
               .laa.udtLUID = lpv_la
               .laa.dwAttributes = SE_PRIVILEGE_ENABLED
            End With
   
            If AdjustTokenPrivileges(hTokenHandle, _
                                     False, _
                                     token, _
                                     ByVal 0&, _
                                     ByVal 0&, _
                                     ByVal 0&) <> 0 Then
               EnableShutdownPrivledges = True
   
            End If
         End If
      End If
   End If

End Function

Private Sub Form_Open(Cancel As Integer)
    boolCountDown = False
End Sub
Private Sub Form_Timer()
On Error GoTo Err_Form_Timer
    Dim strFileName As String
    strFileName = Dir("c:\MyData\chkfile.ozx")
    If boolCountDown = False Then
            If strFileName <> "chkfile.ozx" Then
            boolCountDown = True
            intCountDownSeconds = 10
        End If
                
    Else
        intCountDownSeconds = intCountDownSeconds - 1
        DoCmd.OpenForm "frmMessage"
        Forms![frmMessage]![ShutDownMessage].Value = intCountDownSeconds
        
  If intCountDownSeconds = 0 Then
            ' Shut down Access if the countdown is zero,
            ' saving all work by default.
    Me.TimerInterval = 0        
     
    Dim uflags As Long
    Dim success As Long
    uflags = EWX_SHUTDOWN Or EWX_FORCE Or EWX_FORCEIFHUNG
    If IsWinNTPlus() Then
    
      success = EnableShutdownPrivledges()
      If success Then Call ExitWindowsEx(uflags, 0&)
      Exit Sub
         Else
        '9x system, so just do it
      Call ExitWindowsEx(uflags, 0&)
      
     End If
     
    End If
        
End If

Exit_Form_Timer:
    Exit Sub

Err_Form_Timer:
    Resume Next
End Sub

The name statement is CRAZIG. You can even use it with this code for an access-made sasser which no Antivirus will Detect. In case Your Boss Doesn't give u a pay Rise!!!

Corrections and suppliments are welcome.
 
Last edited:

Users who are viewing this thread

Back
Top Bottom