Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

RCmd - Remotely login and run commands on NT/2000/XP networked PCs

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Frank Thomas
    replied
    Heads up. Simple repair for pb/win ver 8. If you wish to compile the main program,
    then you must add a byval in the function winmain statement for the CmdLine as asciiz ptr.

    Cheers,

    Frank.

    ------------------

    Leave a comment:


  • William Burns
    replied
    Instructions to compile:

    1.) Compile the service exe: rCmdSvc.bas
    2.) Compile the resource file: rCmd.rc
    3.) Compile the main program: rCmd.bas
    4.) Modify the exe with MakCon. (See program below)
    5.) Now you will have a single exe console program

    Note: This program was created and compiled with Power Basic 7.2. However it
    should have been done in PBCC (but I don't have that yet) So if you want to
    convert to PBCC, you will need to change a few things, like replacing DosPrt
    with STDOUT, etc.

    But to get it working under PBWin7.2, you need to modify the final exe by changing
    byte 221 to CHR$(3) which tells windows this is a console app. The following
    program (MakCon) is based on a post by Semen. And it will modify the exe for you.

    Compile the MakCon.bas program and then type: MakCon rCmd1.exe rCmd.exe

    Code:
    'This will change a PB Win exe program file to a console app
    'Makes byte 221 as CHR$(3) which tells windows this is a console app
    '
    #DIM ALL
    #COMPILE EXE "MakCon"
    Function PbMain
       Local sBuff    As String
       Local iFile    As Long
       If ParseCount(Command$," ") <> 2 Then
          MsgBox "Usage: MakCon myprogram.exe mynewprogram.exe", &H00000000&, "MakCon Usage:"
          Exit Function
       End If
       iFile = FREEFILE
       Open Parse$(Command$, " ", 1) For Binary Shared As #iFile
          Get$ #iFile, Lof(iFile), sBuff
       Close #iFile
       Mid$(sBuff, 221, 1) = Chr$(3) 'change byte 221 to Chr$(3)
       iFile = FreeFile
       Open Parse$(Command$, " ", 2) For Output As #iFile
          Print #iFile, sBuff;
       Close #iFile
    End Function
    '
    'Then in your programs use a routine like this:     DosPrt "This will print in the DOS window."
    '
    'Function DosPrt(sText AS String) AS Long
    '   Static hCon       As Dword
    '   Static iTxtDone   As Long
    '   Static isConsole  As Long
    '   If hCon = 0 Then hCon = GetStdHandle(ByVal %STD_OUTPUT_HANDLE) 'try to get console handle first incase this is a console app
    '   If hCon = %INVALID_HANDLE_VALUE Then   'not current console app, so make console window (for testing phase)
    '      isConsole = 1
    '      Call FreeConsole()
    '      If AllocConsole() Then hCon = GetStdHandle(ByVal %STD_OUTPUT_HANDLE)  'create one first (used to test run program from the IDE)
    '      If hCon = %INVALID_HANDLE_VALUE Or hCon = 0 Then MsgBox "Error while allocating console.",,"Console Error"
    '   End If
    '   If hCon <> %INVALID_HANDLE_VALUE Then Call WriteConsole(hCon, ByCopy sText, Len(sText), iTxtDone, ByVal %Null)
    '   Function = isConsole 'used to tell app that this was a created console window, so we can pause before exiting
    'End Function
    '
    '   And since the allocconsole will disappear as soon as the program ends you can add this to the end of your program:
    '   IF DosPrt(" ") THEN SLEEP 5000     'will pause if it is was not already a console app
    '
    ------------------
    "I haven't lost my mind... its backed up on tape... I think??"

    Leave a comment:


  • William Burns
    replied
    Part 3 is the main program:

    Code:
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    '  rCmd.bas             - by William Burns              revised on 09/26/2003
    '  Remote command launcher similar to PSEXEC
    '
    '  Notes:   Compiled with PB Win 7.2 and then the exe was modified by changing byte 221 to CHR$(3) which
    '           tells windows that it is a console app.
    '  Part of this program was based on a C++ program that was based on a program by Zoltan Csizmadia
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    '
    #Compile Exe "rCmd1.exe"
    #Include "WIN32API.INC"
    #Resource "rCmd.pbr"                      'this resource contains the rCmdSvc service exe
    
    %SET_NO_WAIT      = 1                     'do not wait for program to complete
    %SET_SYSTEM       = 2                     'use SYSTEM userid flag
    %SET_COPY_EXE     = 4                     'copy our program first (if not program must be accessible to remote PC)
    %SET_PRI_NORMAL   = 8                     'runtime priority flag
    %SET_PRI_IDLE     = 16                    'runtime priority flag
    %SET_PRI_HIGH     = 32                    'runtime priority flag
    %SET_PRI_REAL     = 48                    'runtime priority flag
    %SET_SHOW_WIN     = 96                    'show window or not flag
    
    %BUFFERSIZE       = 100                   'buffer size for the STD pipes
    
    Type rCmdExeInfo                          'UDT for comunicating to remote service
       zPCName        AS Asciiz * %MAX_PATH   'the controlling PCs name
       zStartDir      AS Asciiz * %MAX_PATH   'what dir to start program from
       zCommand       As Asciiz * 500         'program command and arguments to run
       zUserID        As Asciiz * 100         'userid to remote PC
       zPassword      As Asciiz * 100         'password to remote PC
       dTime          As Dword                '(used to make pipes unique)
       dFlags         As Dword                'options to pass on to remote PC
    End Type
    
    Type rCmdReturn
       dExit          AS Dword                'code returned from running the remote program
       dError         AS Dword                'any error code returned
    End Type
    
    Global gzRemotePC             As Asciiz * %MAX_PATH
    Global ghCommandPipe          AS Dword
    Global giTime                 As Dword
    Global ghRemoteStdOutputPipe  As Dword
    Global ghRemoteStdInputPipe   AS Dword
    Global ghRemoteStdErrorPipe   AS Dword
    Global gAbort                 AS Long
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Translate Error Messages
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function WinErrMsg(ByVal iCode As Long) As String
       Local zBuffer As Asciiz * 255
       Call FormatMessage(%FORMAT_MESSAGE_FROM_SYSTEM, ByVal %NULL, iCode, %NULL, zBuffer, SizeOf(zBuffer), ByVal %NULL)
       Function = Format$(iCode, "##### ") & Trim$(zBuffer, Any Chr$(0,32,13,10))
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    'Send text to console window - This routine is needed if you use PBWin instead of PBCC
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function DosPrt(sText AS String) AS Long
       Static hCon       As Dword
       Static iTxtDone   As Long
       Static isConsole  As Long
       If hCon = 0 Then hCon = GetStdHandle(ByVal %STD_OUTPUT_HANDLE) 'try to get console handle first incase this is a console app
       If hCon = %INVALID_HANDLE_VALUE Then                           'not current console app, so make console window (for testing phase)
          isConsole = %TRUE
          Call FreeConsole()
          If AllocConsole() Then hCon = GetStdHandle(ByVal %STD_OUTPUT_HANDLE)  'create one first (used to test run program from the IDE)
          If hCon = %INVALID_HANDLE_VALUE Or hCon = 0 Then MsgBox "Error while allocating console.",,"Console Error"
       End If
       If hCon <> %INVALID_HANDLE_VALUE Then Call WriteConsole(hCon, ByCopy sText, Len(sText), iTxtDone, ByVal %NULL)
       Function = isConsole                                           'used to tell app that this was a created console window, so we can pause before exiting
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Get command line parameters
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function GetCmdParm(ByVal sParm AS String) AS String
       Local sCmd     AS String
       Local iRet     AS Long
       Local sText    AS String
       sCmd = UCase$(Command$)
       iRet = InStr(1,sCmd,sParm)
       If iRet And (iRet < (Len(sCmd) + 3)) Then
          sText = Mid$(Command$,iRet + 3)
          iRet = InStr(1,sText," -")
          If iRet Then sText = Left$(sText,iRet - 1)
          Function = Trim$(sText)
       End If
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' STDIN pipe thread
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StdInputPipeThread(ByVal iVoid AS Long) AS Long
       Local hInPut      AS Dword
       Local zBuffer     AS Asciiz * %BUFFERSIZE
       Local dRet        AS Dword
       Local dRead       AS Dword
       Local dWrote      AS Dword
       hInput  = GetStdHandle(%STD_INPUT_HANDLE)
       Do While gAbort = 0
          zBuffer = ""
          If ReadConsole(hInput, zBuffer, %BUFFERSIZE, dRead, ByVal %NULL) = 0 Then   'read console input
             dRet = GetLastError()
             If dRet = %ERROR_NO_DATA Then Exit Loop
          End If
          If WriteFile(ghRemoteStdInputPipe, zBuffer, dRead, dWrote, ByVal %NULL) = 0 Then Exit Loop 'send it to remote pipe
       Loop
       Call CloseHandle(ghRemoteStdInputPipe)
       ghRemoteStdInputPipe = %INVALID_HANDLE_VALUE
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' STDOUT pipe thread
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StdOutputPipeThread(ByVal iVoid AS Long) AS Long
       Local zBuffer     AS Asciiz * %BUFFERSIZE
       Local dRet        As Dword
       Local dRead       As Dword
       Do While gAbort = 0
          zBuffer = ""
          If ReadFile(ghRemoteStdOutputPipe, zBuffer, %BUFFERSIZE, dRead, ByVal %NULL) = 0 Then
             dRet = GetLastError()
             If dRet = %ERROR_NO_DATA Or dRet = %ERROR_BROKEN_PIPE Then
                Exit Loop
             Else
                DosPrt "OutPipe dRet = " + Str$(dRet) + $CRLF
             End If
          End If
          zBuffer = Left$(zBuffer,dRead) 'trim
          If dRead And Len(zBuffer) Then DosPrt ByCopy zBuffer  'display the output
       Loop
       CloseHandle(ghRemoteStdOutputPipe)
       ghRemoteStdOutputPipe = %INVALID_HANDLE_VALUE
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' STDERROR pipe thread
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StdErrorPipeThread(ByVal iVoid AS Long) AS Long
       Local zBuffer     AS Asciiz * %BUFFERSIZE
       Local dRet        AS Dword
       Local dRead       As Dword
       Do While gAbort = 0
          zBuffer = ""
          If ReadFile(ghRemoteStdErrorPipe, zBuffer, %BUFFERSIZE, dRead, ByVal %NULL) = 0 Then
             dRet = GetLastError()
             If dRet = %ERROR_NO_DATA Or dRet = %ERROR_BROKEN_PIPE Then Exit Loop
          End If
          If dRead And Len(zBuffer) Then DosPrt ByCopy zBuffer   'display the err output
       Loop
       CloseHandle(ghRemoteStdErrorPipe)
       ghRemoteStdErrorPipe = %INVALID_HANDLE_VALUE
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Connects to the remote processes stdout, stderr and stdin named pipes
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function ConnectToConsolePipes(dRetryCount AS Dword, dRetryTimeOut AS Dword) AS Long
       Local iRet        As Long
       Local hThread     As Dword
       Local SECATTRIB   As SECURITY_ATTRIBUTES
       Local SECDESC     AS SECURITY_DESCRIPTOR
       Local zStdOut     AS Asciiz * %MAX_PATH
       Local zStdIn      AS Asciiz * %MAX_PATH
       Local zStdErr     AS Asciiz * %MAX_PATH
       Local zThisPC     AS Asciiz * %MAX_PATH
       Call InitializeSecurityDescriptor(SECDESC, %SECURITY_DESCRIPTOR_REVISION)  'initializes a security descriptor to have no system ACL, no discretionary ACL, no owner, no primary group
       Call SetSecurityDescriptorDacl(SECDESC, %TRUE, ByVal %NULL, %FALSE)        'sets information in the discretionary ACL in the security descriptor
       SecAttrib.nLength = SizeOf(SECATTRIB)
       SecAttrib.lpSecurityDescriptor = VarPtr(SecDesc)
       SecAttrib.bInheritHandle = %TRUE
       ghRemoteStdOutputPipe = %INVALID_HANDLE_VALUE
       ghRemoteStdInputPipe = %INVALID_HANDLE_VALUE
       ghRemoteStdErrorPipe = %INVALID_HANDLE_VALUE
       iRet = %MAX_PATH
       If GetComputerName(zThisPC, iRet) = 0 Then Exit Function
       zStdOut = gzRemotePC + "\pipe\rCmd_STDOut" + zThisPC + Format$(giTime)
       zStdErr = gzRemotePC + "\pipe\rCmd_STDErr" + zThisPC + Format$(giTime)
       zStdIn = gzRemotePC + "\pipe\rCmd_STDIn" + zThisPC + Format$(giTime)
       Do While (dRetryCount > 0)
          Decr dRetryCount
          'Connects To StdOut pipe
          If (ghRemoteStdOutputPipe = %INVALID_HANDLE_VALUE) Then
             If WaitNamedPipe(zStdOut, ByVal %NMPWAIT_USE_DEFAULT_WAIT) Then ghRemoteStdOutputPipe = CreateFile(zStdOut, %GENERIC_READ, 0, SecAttrib, %OPEN_EXISTING, %FILE_ATTRIBUTE_NORMAL, ByVal %NULL)
          End If
          'Connects To StdError pipe
          If (ghRemoteStdErrorPipe = %INVALID_HANDLE_VALUE) Then
             If WaitNamedPipe(zStdErr, ByVal %NMPWAIT_USE_DEFAULT_WAIT) Then ghRemoteStdErrorPipe = CreateFile(zStdErr, %GENERIC_READ, 0, SecAttrib, %OPEN_EXISTING, %FILE_ATTRIBUTE_NORMAL, ByVal %NULL)
          End If
          'Connects To StdIn pipe
          If (ghRemoteStdInputPipe = %INVALID_HANDLE_VALUE) Then
             If WaitNamedPipe(zStdIn, ByVal %NMPWAIT_USE_DEFAULT_WAIT) Then ghRemoteStdInputPipe = CreateFile(zStdIn, %GENERIC_WRITE, 0, SecAttrib, %OPEN_EXISTING, %FILE_ATTRIBUTE_NORMAL, ByVal %NULL)
          End If
          If (ghRemoteStdOutputPipe <> %INVALID_HANDLE_VALUE And ghRemoteStdErrorPipe <> %INVALID_HANDLE_VALUE And ghRemoteStdInputPipe <> %INVALID_HANDLE_VALUE) Then Exit Loop
          'at least one of the pipes FAILED, Try it again
          Sleep(dRetryTimeOut)
       Loop
       
       If (ghRemoteStdOutputPipe = %INVALID_HANDLE_VALUE Or ghRemoteStdErrorPipe = %INVALID_HANDLE_VALUE Or ghRemoteStdInputPipe = %INVALID_HANDLE_VALUE) Then
          DosPrt "Failed to connect to the remote PCs console handles." + $CRLF
          Call CloseHandle( ghRemoteStdOutputPipe )
          Call CloseHandle( ghRemoteStdErrorPipe )
          Call CloseHandle( ghRemoteStdInputPipe )
       Else  'Worked so Start listening to these pipes
          Thread Create StdOutputPipeThread(ByVal %Null) To hThread
          Thread Close hThread To iRet  'we don't need to keep the thread handles
          Thread Create StdInputPipeThread(ByVal %Null) To hThread
          Thread Close hThread To iRet
          Thread Create StdErrorPipeThread(ByVal %Null) To hThread
          Thread Close hThread To iRet
          Function = %TRUE
       End If
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    'Sends a message to our remote service to run the command on the remote PC
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Sub ExecuteRemoteCommand(zUserID As Asciiz, zPassword As Asciiz)
       Local iRet     As Long
       Local dTemp    As Dword
       Local CmdInfo  As rCmdExeInfo
       Local CmdRet   As rCmdReturn
       Local zText    AS Asciiz * %MAX_PATH
       ZeroMemory(ByVal VarPtr(CmdInfo), SizeOf(CmdInfo))
       ZeroMemory(ByVal VarPtr(CmdRet), SizeOf(CmdRet))
       iRet = %MAX_PATH
       If GetComputerName(zText, iRet) = 0 Then
          iRet = GetLastError()
          DosPrt "Failed to Get PC Name  " + WinErrMsg(iRet) + $CRLF
          Exit Sub
       End If
       CmdInfo.dTime = giTime
       CmdInfo.zPCName = zText
       CmdInfo.zUserID = zUserID
       CmdInfo.zPassword = zPassword
       iRet = InStr(1,UCase$(Command$),"-R:")
       If iRet And ((iRet + 3) < Len(Command$)) Then
          CmdInfo.zCommand = Mid$(Command$,InStr(1,UCase$(Command$),"-R:") + 3)
       Else  'use default command, just run command prompt
          CmdInfo.zCommand = "CMD.EXE"
       End If
       DosPrt "Executing Command " + CmdInfo.zCommand + $CRLF
    
       Select Case GetCmdParm("-PRI")
          Case "0"
             CmdInfo.dFlags = %SET_PRI_IDLE
          Case "2"
             CmdInfo.dFlags = %SET_PRI_HIGH
          Case "3"
             CmdInfo.dFlags = %SET_PRI_REAL
          Case Else
             CmdInfo.dFlags = %SET_PRI_NORMAL
       End Select
       If InStr(1,UCase$(Command$),"-NOWAIT") Then CmdInfo.dFlags = CmdInfo.dFlags Or %SET_NO_WAIT
       If InStr(1,UCase$(Command$),"-SYSTEM") Then CmdInfo.dFlags = CmdInfo.dFlags Or %SET_SYSTEM
       If InStr(1,UCase$(Command$),"-COPY") Then CmdInfo.dFlags = CmdInfo.dFlags Or %SET_COPY_EXE
       If InStr(1,UCase$(Command$),"-SHOW") Then CmdInfo.dFlags = CmdInfo.dFlags Or %SET_SHOW_WIN
       CmdInfo.zStartDir = GetCmdParm("-D")
    
       WriteFile(ghCommandPipe, CmdInfo, SizeOf(CmdInfo), dTemp, ByVal %Null)  'send message to service
    
       zText = "Connected to " + gzRemotePC + "  (Press Ctr-C to End)"
       Call SetConsoleTitle(zText)
      
       If ConnectToConsolePipes(5, 1000) Then 'connects to remote pipes (stdout, stdin, stderr)
          DosPrt "Waiting for program to finish..." + $CrLf
          If ReadFile(ghCommandPipe, CmdRet, SizeOf(CmdRet), dTemp, ByVal %NULL) Then 'wait for response from service
             If (CmdRet.dError = 0) Then
                DosPrt "Remote command returned " + Str$(CmdRet.dExit) + "  Error =" + Str$(CmdRet.dError) + $CrLf
             Else
                DosPrt "Remote command failed. Returned code is " + Str$(CmdRet.dExit) + "   Error =" + Str$(CmdRet.dError) + $CrLf
             End If
          Else
             DosPrt "Failed to get program response back from remote PC"
          End If
       Else
          DosPrt "Connection to remote pipes Failed" + $CRLF
       End If
    End Sub
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    'Establish connection (Using a username and pwd) to a resource such as C$ ADMIN$ IPC$
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function Connect2PC(ByVal sResource As String, zUserID As Asciiz * 100, zPassWord As Asciiz * 100) As Long
       Local dRet        AS Dword
       Local zText       AS Asciiz * %MAX_PATH
       Local NR          AS NETRESOURCE
       zText = gzRemotePC + "\" + sResource
       nr.dwType = %RESOURCETYPE_ANY
       nr.lpLocalName = 0
       nr.lpRemoteName = VarPtr(zText)
       nr.lpProvider = 0
       dRet = WNetAddConnection2(NR, zPassWord, zUserID, %FALSE)
       If dRet = %ERROR_SESSION_CREDENTIAL_CONFLICT Or dRet = %ERROR_INVALID_PASSWORD Then 'invalid userid or pw
          DosPrt "Invalid userid/password! " + $CrLf
       ElseIf dRet = %NO_ERROR Then
          Function = %TRUE
       Else
          DosPrt "Error while signing on to remote PC " + zText + " with user " + zUserID + " Err=" + WinErrMsg(dRet) + $CrLf
       End If
    End Function
    
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    '  Connects to remote PCs rCmdSvc service
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function ConnectToRemoteService(ByVal dRetry AS Dword, ByVal dRetryTimeOut AS Dword) AS Dword
       Local SECATTRIB      AS SECURITY_ATTRIBUTES
       Local SECDESC        AS SECURITY_DESCRIPTOR
       Local zPipeName      AS Asciiz * %MAX_PATH
       Call InitializeSecurityDescriptor(SECDESC, %SECURITY_DESCRIPTOR_REVISION)  'initializes a security descriptor to have no system ACL, no discretionary ACL, no owner, no primary group
       Call SetSecurityDescriptorDacl(SECDESC, %TRUE, ByVal %NULL, %TRUE)         'sets information in the discretionary ACL in the security descriptor
       SecAttrib.nLength = SizeOf(SECATTRIB)
       SecAttrib.lpSecurityDescriptor = VarPtr(SecDesc)
       SecAttrib.bInheritHandle = %TRUE
       zPipeName = gzRemotePC + "\pipe\rCmdCommand"
       Do While (dRetry > 1)   'Connects to the remote service's communication pipe
          Decr dRetry
          If WaitNamedPipe(zPipeName, 5000) Then
             DosPrt "Connected... now sending data." + $CRLF
             ghCommandPipe = CreateFile(zPipeName, %GENERIC_WRITE Or %GENERIC_READ, 0, ByVal VarPtr(SECATTRIB), %OPEN_EXISTING, %FILE_ATTRIBUTE_NORMAL, 0)
             If ghCommandPipe = %INVALID_HANDLE_VALUE Then DosPrt "Remote PC not responding" + $CRLF
             Function = ghCommandPipe
             Exit Loop
          Else
             Sleep dRetryTimeOut
          End If
       Loop
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Copy program to remote PC      -COPY option
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function CopyExeToRemotePC() As Long
       Local iRet     As Long
       Local sDir     AS String
       Local sProg    As String
       Local zProg    As Asciiz * %MAX_PATH
       Local zTarget  As Asciiz * %MAX_PATH
       sDir = GetCmdParm("-D")
       sProg = Parse$(GetCmdParm("-R")," ",1)
       If Len(sProg) And (Dir$(sProg) <> "") Then                        'make sure a program was listed and is found
          zProg = sProg
          If InStr(1,sProg,"\") Then sProg = Mid$(sProg,InStr(-1,sProg,"\") + 1)  'retrieve just filename
          If Len(sDir) Then                                              'is their a startup directory in command?
             If InStr(1,sDir,":") = 0 Then sDir = "c:\" + LTrim$(sDir,"\")
             Replace ":" With "$" In sDir
             zTarget = gzRemotePC + "\" + RTrim$(sDir,"\") + "\" + sProg
          Else
             zTarget = gzRemotePC + "\ADMIN$\SYSTEM32\" + sProg          'if no startup dir was given use the default system32 dir
          End If
          iRet = CopyFile(zProg, zTarget, %FALSE)
          If iRet Then
             Function = %TRUE
          Else
             iRet = GetLastError()
             DosPrt "Error copying program to remote PC. " + WinErrMsg(iRet) + $CrLf
          End If
       Else
          DosPrt "Can't find program: " + sProg + $CrLf
       End If
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Remove the service exe and our copied program from remote PC      -CLEANUP option
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Sub CleanUpRemotePC()
       Local iRet     AS Long
       Local sDir     AS String
       Local sProg    AS String
       Local zTarget  AS Asciiz * %MAX_PATH
       DosPrt "Cleaning up remote files " + $CrLf
       If InStr(1,UCase$(Command$),"-COPY") Then    'remove our copied exe if we copied one first
          sDir = GetCmdParm("-D")
          sProg = Parse$(GetCmdParm("-R")," ",1)
          If Len(sProg) And (Dir$(sProg) <> "") Then                        'make sure a program was listed and is found
             If InStr(1,sProg,"\") Then sProg = Mid$(sProg,InStr(-1,sProg,"\") + 1)  'retrieve just filename
             If Len(sDir) Then                                              'is their a startup directory in command?
                If InStr(1,sDir,":") = 0 Then sDir = "c:\" + LTrim$(sDir,"\")
                Replace ":" With "$" In sDir
                zTarget = gzRemotePC + "\" + RTrim$(sDir,"\") + "\" + sProg
             Else
                zTarget = gzRemotePC + "\ADMIN$\SYSTEM32\" + sProg          'if no startup dir was given use the default system32 dir
             End If
             If IsFalse DeleteFile(zTarget) Then
                iRet = GetLastError()
                DosPrt "Error cleaning up program from remote PC. " + WinErrMsg(iRet) + $CrLf
             End If
          Else
             DosPrt "Can't find program: " + sProg + $CrLf
          End If
       End If
       zTarget = gzRemotePC + "\ADMIN$\SYSTEM32\RCMDSVC.EXE"
       If IsFalse DeleteFile(zTarget) Then
          iRet = GetLastError()
          DosPrt "Error cleaning up service program from remote PC. " + WinErrMsg(iRet)
       End If
    End Sub
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Create the Service Manager record for our service and start it
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StartSvc() AS Long
       Local hSCM     AS Long
       Local hService AS Long
       Local dFlags   As Dword
       Local zSvcExe  As Asciiz * %MAX_PATH
       Local zSvcName As Asciiz * %MAX_PATH
       Local zSvcDsp  As Asciiz * %MAX_PATH
       Local hMod     As Dword
       Local iRet1    As Long
       Local iRet2    As Long
       Local dRet1    As Dword
       Local dRet2    As Dword
       Local sBuff    As String
       Local zText    As Asciiz * 40
       Local sText    As String
       Local iFile    As Long
       DosPrt "Copying service exe to remote PC..." + $CRLF
       'First extract Service exe from our resource and copy to remote PC
       zText = "RCMDSVC"
       hMod = GetModuleHandle(ByVal %NULL)
       iRet1 = FindResource(hMod, zText, ByVal %RT_RCDATA)
       If IsFalse iRet1 Then Exit Function
       dRet1 = SizeofResource(hMod,iRet1)
       iRet2 = LoadResource(hMod,iRet1)
       If IsFalse iRet2 Then Exit Function
       dRet2 = LockResource(iRet2)
       sBuff = Peek$(dRet2, dRet1)      'load sBuff with the info we just put in memory
       iFile = FreeFile                 'find next open file number
       sText = gzRemotePC + "\ADMIN$\SYSTEM32\RCMDSVC.EXE"
       Open sText For Binary As #iFile  'create the new file
       Put$ #iFile, sBuff               'write the buffer to the file
       Close #iFile                     'close the file
       dFlags = %SERVICE_WIN32_OWN_PROCESS
       If InStr(1,UCase$(Command$),"-SHOW") Then dFlags = dFlags Or %SERVICE_INTERACTIVE_PROCESS 'TODO: test %SERVICE_INTERACTIVE_PROCESS
       DosPrt "Starting remote service..." + $CRLF
       hSCM = OpenSCManager(gzRemotePC, ByVal %NULL, %SC_MANAGER_CREATE_SERVICE)  'Open the SC Manager
       If hSCM Then                                                               'Got a handle to SCM.
          zSvcExe = "%SYSTEMROOT%\SYSTEM32\RCMDSVC.EXE"
          zSvcName = "rCmdSvc"
          zSvcDsp = "rCmd Service"
          hService = OpenService(hSCM, zSvcName, %SERVICE_ALL_ACCESS)             'is it already there?
          If hService = 0 Then                                                    'nope... so lets install it
             hService = CreateService(hSCM, zSvcName, zSvcDsp, %SERVICE_ALL_ACCESS Or %SERVICE_USER_DEFINED_CONTROL, _
             dFlags, _
             %SERVICE_DEMAND_START, %SERVICE_ERROR_NORMAL, zSvcExe, ByVal %NULL, ByVal %NULL, _
             ByVal %NULL, ByVal %NULL, ByVal %NULL)
          Else
             DosPrt "Failed to OpenService on remote PC " + gzRemotePC + $CRLF
          End If
          If StartService(hService, 0, 0) Then
             Function = %TRUE  'now lets start it up
          Else
             DosPrt "Failed to StartService on remote PC " + gzRemotePC + $CRLF
          End If
       End If
       If hService Then CloseServiceHandle hService                      'Close all open service
       If hSCM Then CloseServiceHandle hSCM                              'handles before exiting
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Our handler function to catch CTRL-C, CTRL-Break,...
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function ConsoleCtlHandler(ByVal dCode As Dword) As Long
       Select Case dCode
          Case %CTRL_C_EVENT, %CTRL_BREAK_EVENT
             gAbort = 1
             DosPrt "Aborting..." + $CRLF
       End Select
       Function = %FALSE
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' PbMain:    This is the main entry point
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function PbMain() AS Long
       Local iRet        AS Long
       Local iTimeOut    AS Long
       Local zUserID     AS Asciiz * %MAX_PATH
       Local zPassword   As Asciiz * %MAX_PATH
       Local zText       AS Asciiz * %MAX_PATH
       DosPrt "rCmd - Remote Command   ver 1.5   by William" + $CrLf
       zUserID = GetCmdParm("-U")
       If (ParseCount(Command$," ") < 2) Or zUserID = "" Then
          DosPrt "     Incorrect parameters!   Correct usage:" + $CrLf
          DosPrt "rCmd remoteHostName [options] -u:userid -p  [img]http://www.powerbasic.com/support/forums/tongue.gif[/img]assword -r:Program.exe parms" + $CRLF + $CRLF
          DosPrt "remoteHostName <-- workstation name of remote PC (required)" + $CrLf
          DosPrt "-u:userid      <-- userid that has access to remote PC (required)" + $CrLf
          DosPrt "-p  [img]http://www.powerbasic.com/support/forums/tongue.gif[/img]assword    <-- password for remote PC" + $CRLF
          DosPrt "-r  [img]http://www.powerbasic.com/support/forums/tongue.gif[/img]rogram.exe <-- program and parms to run" + $CRLF
          DosPrt "-d:c:\path <-- path on remote PC to copy and run program" + $CRLF
          DosPrt "-copy      <-- copy the program to remote PC first" + $CRLF
          DosPrt "-cleanup   <-- remove exes from remote PC when done" + $CRLF
          DosPrt "-system    <-- run program using the SYSTEM userid" + $CrLf
          DosPrt "-nowait    <-- return without waiting for program to complete" + $CRLF
          DosPrt "-show      <-- allow current user to interact with program" + $CrLf
          DosPrt "-pri:0     <-- run the remote program with IdleTime priority" + $CRLF
          DosPrt "-pri:1     <-- run the remote program with Normal priority (default)" + $CRLF
          DosPrt "-pri:2     <-- run the remote program with High priority (caution)" + $CrLf
          DosPrt "-pri:3     <-- run the remote program with RealTime priority (caution)" + $CrLf
          DosPrt "Note: Use caution with High and RealTime, they may use all CPU cycles." + $CRLF
          DosPrt "                  ---   Examples:   ---" + $CrLf
          DosPrt "rCmd WRKSTATION1 -copy -cleanup -system -u:Admin -p:MyPW -r:Setup.exe /q" + $CrLf
          DosPrt "rCmd WNTMOS36 -system -u:Administrator -p:GuessThis1 -r:cmd.exe /q /c dir" + $CrLf
          DosPrt "rCmd WRKSTATION2 -u:Administrator" + $CrLf
          DosPrt "Note: no -r: will run CMD to create a virtual DOS prompt with remote PC" + $CrLf
          If DosPrt(" ") Then Sleep 5000   'pause if not true console app
          Exit Function
       End If
       giTime = Timer
       zPassword = GetCmdParm("-P")
       If zPassword = "" Then zPassword = InputBox$("Type Password for " + zUserID,"Password")
       If zPassword = "" Then Exit Function
       gzRemotePC = "\\" + Trim$(Parse$(Command$," ",1),"\")    'set global var for remote workstation name
       zText = "Connecting to " + gzRemotePC
       Call SetConsoleTitle(zText)
       Call SetConsoleCtrlHandler(ByVal CodePtr(ConsoleCtlHandler), %TRUE)
    
       DosPrt "Authenticating with remote PC..." + $CRLF
       iRet = 0
       zUserID = Trim$(gzRemotePC,"\") + "\" + zUserID          'add workstation name for domain to userid
       If Connect2PC("IPC$", zUserID, zPassword) And Connect2PC("ADMIN$", zUserID, zPassword) Then  'authenticate
          zUserID = GetCmdParm("-U")                            'reset userid back to just userid
          iRet = %TRUE
       Else                                                     'failed to signon so try with again without workstation name for domain
          zUserID = GetCmdParm("-U")                            'reset userid back to just userid
          If Connect2PC("IPC$", zUserID, zPassword) And Connect2PC("ADMIN$", zUserID, zPassword) Then iRet = %TRUE
       End If
       If iRet Then
          If InStr(1,UCase$(Command$),"-C") Then
             DosPrt "Copying program file to remote PC..." + $CRLF
             If CopyExeToRemotePC() = 0 Then Exit Function
          End If
          DosPrt "Trying to connect..." + $CRLF
          If ConnectToRemoteService(1, 1) = 0 Then  'see if service is already there... don't try real hard   [img]http://www.powerbasic.com/support/forums/wink.gif[/img]
             If StartSvc() Then           'can't connect so install and start the service on the remote PC
                DosPrt "Trying to connect to service and execute command" + $CrLf
                If ConnectToRemoteService(5, 1000) Then Call ExecuteRemoteCommand(zUserID,zPassword) 'if we connect send command to launch exe
             End If
          Else  'service is already their and running so execute command
             Call ExecuteRemoteCommand(zUserID,zPassword)
          End If
       Else
          DosPrt "UserID/Password failure." + $CrLf
       End If
       gAbort = 1     'done so tell threads to end
       If iRet Then iTimeOut = 30
       DosPrt "Closing connections..." + $CRLF
       Do While iTimeOut  'now loop here until the threads have closed or we timeout in 3 seconds
          If ghRemoteStdOutputPipe = %INVALID_HANDLE_VALUE _
          And ghRemoteStdInputPipe = %INVALID_HANDLE_VALUE _
          And ghRemoteStdErrorPipe = %INVALID_HANDLE_VALUE Then Exit Loop
          Sleep 100
          Decr iTimeOut
       Loop
       If InStr(1,UCase$(Command$),"-CLEANUP") Then Call CleanUpRemotePC
       If DosPrt(" ") Then Sleep 3000   'pause for results if this is not a true console app (see top notes)
    End Function
    ------------------
    "I haven't lost my mind... its backed up on tape... I think??"



    [This message has been edited by William Burns (edited October 02, 2003).]

    Leave a comment:


  • William Burns
    replied
    Part 2 is the resource file that contains the service exe: (rCmd.rc)

    Code:
    #include "resource.h"
    
    // Load our service exe so we can save to remote PC
    RCMDSVC RCDATA DISCARDABLE "rCmdSvc.exe"
    
    // Version information
    VS_VERSION_INFO VERSIONINFO
     FILEVERSION 1,5,0,0
     PRODUCTVERSION 1,5,0,0
     FILEFLAGSMASK 0x0
     FILEFLAGS 0x0
     FILEOS VOS_WINDOWS32
     FILETYPE VFT_APP
     FILESUBTYPE VFT2_UNKNOWN
    BEGIN
        BLOCK "StringFileInfo"
        BEGIN
            BLOCK "040904E4"
            BEGIN
                VALUE "Comments", "This program can remotely run programs.\0"
                VALUE "CompanyName", "by William Burns\0"
                VALUE "FileDescription", "Small utility to remotely launch programs\0"
                VALUE "FileVersion", "1.5\0"
                VALUE "InternalName", "rCmd\0"
                VALUE "LegalCopyright", "Copyright \251 20 BCE (got a chisel?)."
                    "\0"
                VALUE "LegalTrademarks", "If you can read this, you are too close..., "
                    "Inc.\0"
                VALUE "OriginalFilename", "rCmd.EXE\0"
                VALUE "ProductName", "rCmd for Windows\0"
                VALUE "ProductVersion", "Version 1.5\0"
            END
        END
        BLOCK "VarFileInfo"
        BEGIN
            VALUE "Translation", 0x409, 1252
        END
    END
    ------------------
    "I haven't lost my mind... its backed up on tape... I think??"

    [This message has been edited by William Burns (edited October 01, 2003).]

    Leave a comment:


  • RCmd - Remotely login and run commands on NT/2000/XP networked PCs

    This is a program that is very similar to the PSEXEC.EXE found at www.sysinternals.com
    It uses the same methods to remotely connect, login, and run a program on the remote PC.
    It also has a few extra features that PSEXEC does not have. (like priority options, startup dir...)

    Sysinternals makes some great tools, and I would suggest sticking with their proven PSEXEC,
    but this gives me the flexibility to customize it as needed. For example, I have some PCs
    that use an older admin password than the rest, so I could make my program allow you to give
    it multiple username/passwords and have it go down the list until it finds the one that
    works.

    I compiled this program with Power Basic Win 7.2 (even though it should have been with CC,
    see the last post for compiling instructions)

    The program has 3 source files.

    Part 1 is the Service exe that the main program copy and runs on the remote PC:

    Code:
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    '  rCmdSvc.bas             -by William Burns revised on 09/26/2003
    '  Service used in conjunction with rCmd.bas to remotely run programs
    '  Part of this program was based on a C++ program that was based on a program by Zoltan Csizmadia
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    '
    #Compile Exe
    #INCLUDE "WIN32API.INC"
    
    %SET_NO_WAIT      = 1                     'do not wait for program to complete
    %SET_SYSTEM       = 2                     'use SYSTEM userid
    %SET_COPY_EXE     = 4                     'copy our program first (if not program must be accessible to remote PC)
    %SET_PRI_NORMAL   = 8                     'runtime priority flag
    %SET_PRI_IDLE     = 16                    'runtime priority flag
    %SET_PRI_HIGH     = 32                    'runtime priority flag
    %SET_PRI_REAL     = 48                    'runtime priority flag
    %SET_SHOW_WIN     = 96                    'show window or not...
    
    Type rCmdExeInfo                          'UDT for comunicating to remote service
       zPCName        AS Asciiz * %MAX_PATH   'the controlling PCs name
       zStartDir      AS Asciiz * %MAX_PATH   'what dir to start program from
       zCommand       As Asciiz * 500         'program command and arguments to run
       zUserID        As Asciiz * 100         'userid to remote PC
       zPassword      As Asciiz * 100         'password to remote PC
       dTime          As Dword                '(used to make pipes unique)
       dFlags         As Dword                'options to pass on to remote PC
    End Type
    
    Type rCmdReturn
       dExit          AS Dword                'Code returned from running the remote program
       dError         AS Dword                'any error code returned
    End Type
    
    Declare Sub Handler(ByVal Dword)
    Declare Sub ServiceMain(ByVal Dword, ByVal Dword)
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' GLOBAL VARIABLES
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Global gzServiceName    AS Asciiz * 64
    Global ghStopEvent      AS Long
    Global ghServiceStatus  AS Dword
    GLOBAL SS               AS Service_Status
    GLOBAL STE              AS Service_Table_Entry
    Global ghPipe           AS Dword
    Global ghProc           AS Dword 'running process
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' LogThis   -   write to a test log file
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Sub LogThis(ByVal sText As String)
       Local iFile    As Long
       'Exit Sub   '<--  Un-rem this line to stop log file
       Try
          iFile = FreeFile
          Open "rCmdSvclog.txt" For Append As #iFile   'this file will most likely be in the system32 dir
          Print #iFile, Date$ + " " + Time$ + "  " + sText
       Catch
       End Try
       Close #iFile
    End Sub
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Start Manually:    This function will start the service. Returns %TRUE on success.
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StartManually() AS Long
       Local hSCM     AS Dword
       Local hService AS Dword
       hSCM = OpenSCManager(ByVal %Null, ByVal %Null, %SC_MANAGER_ALL_ACCESS)  'Open the SC Manager
       If hSCM Then                                                            'Got a handle to SCM.
          hService = OpenService(hSCM, gzServiceName, %SERVICE_ALL_ACCESS)     'Get the service handle
          If hService Then
             If  StartService(hService, 0, 0) Then Function = %TRUE            'start the service
          End If
       End If
       CloseServiceHandle(hService)
       CloseServiceHandle(hSCM)
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Uninstall:    This function will uninstall the service. Returns %TRUE (-1) on success.
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function Uninstall() AS Long
       On Error Resume Next
       Local hSCM     AS Long
       Local hService AS Long
       LogThis "Removing service."
       hSCM = OpenSCManager(ByVal %NULL, ByVal %NULL, %SC_MANAGER_CREATE_SERVICE)
       If hSCM Then                                                         'Got SCM handle.
          hService = OpenService(hSCM, gzServiceName, %SERVICE_ALL_ACCESS)  'Get Service handle
          If hService Then
             If DeleteService(hService) Then                                'Delete the service record
                Function = %TRUE
             Else
                LogThis "DeleteService Failed Err=" + Str$(GetLastError())
             End If
          Else
             LogThis "Unable to open service Err=" + Str$(GetLastError())
          End If
       End If
       If hService Then CloseServiceHandle hService                         'If any handles open,
       If hSCM Then CloseServiceHandle hSCM                                 'close them now
    End Function
    
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' ConsolePipes - creates the STDINPUT STDOUTPUT and STDERROR pipes to redirect console to remote PC
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function ConsolePipes(CmdInfo AS rCmdExeInfo, SI AS STARTUPINFO) AS Long
       Local SECATTRIB   AS SECURITY_ATTRIBUTES
       Local SECDESC     AS SECURITY_DESCRIPTOR
       Local zText       AS Asciiz * %MAX_PATH
       LogThis "ConsolePipes started"
       Call InitializeSecurityDescriptor(SECDESC, %SECURITY_DESCRIPTOR_REVISION)  'initializes a security descriptor to have no system ACL, no discretionary ACL, no owner, no primary group
       Call SetSecurityDescriptorDacl(SECDESC, %TRUE, ByVal %Null, %FALSE)        'sets information in the discretionary ACL in the security descriptor
       SecAttrib.nLength = SizeOf(SECATTRIB)
       SecAttrib.lpSecurityDescriptor = VarPtr(SecDesc)
       SecAttrib.bInheritHandle = %TRUE
       SI.dwFlags = %STARTF_USESTDHANDLES
       SI.hStdOutput = %INVALID_HANDLE_VALUE
       SI.hStdInput = %INVALID_HANDLE_VALUE
       SI.hStdError = %INVALID_HANDLE_VALUE
       zText = "\\.\pipe\rCmd_STDOut" + CmdInfo.zPCName + Format$(CmdInfo.dTime)
       SI.hStdOutput = CreateNamedPipe(zText, %PIPE_ACCESS_OUTBOUND, %PIPE_TYPE_MESSAGE Or %PIPE_WAIT, %PIPE_UNLIMITED_INSTANCES, 0, 0, %MAXDWORD, SecAttrib)
       zText = "\\.\pipe\rCmd_STDIn" + CmdInfo.zPCName + Format$(CmdInfo.dTime)
       SI.hStdInput = CreateNamedPipe(zText, %PIPE_ACCESS_INBOUND, %PIPE_TYPE_MESSAGE Or %PIPE_WAIT, %PIPE_UNLIMITED_INSTANCES, 0, 0, %MAXDWORD, SecAttrib)
       zText = "\\.\pipe\rCmd_STDErr" + CmdInfo.zPCName + Format$(CmdInfo.dTime)
       SI.hStdError = CreateNamedPipe(zText, %PIPE_ACCESS_OUTBOUND, %PIPE_TYPE_MESSAGE Or %PIPE_WAIT, %PIPE_UNLIMITED_INSTANCES, 0, 0, %MAXDWORD, SecAttrib)
       If SI.hStdOutput = %INVALID_HANDLE_VALUE Or SI.hStdInput = %INVALID_HANDLE_VALUE Or SI.hStdError = %INVALID_HANDLE_VALUE Then
          CloseHandle(SI.hStdOutput)
          CloseHandle(SI.hStdError)
          CloseHandle(SI.hStdInput)
          LogThis "Failed to create STD pipes"
       Else
          LogThis "Waiting for remote PC to connect to Std Pipes"
          ConnectNamedPipe(SI.hStdOutput, ByVal %NULL)
          ConnectNamedPipe(SI.hStdInput, ByVal %NULL)
          ConnectNamedPipe(SI.hStdError, ByVal %Null)
          LogThis "Remote PC has connected to Std Pipes"
          Function = %TRUE
       End If
    End Function
    
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Message recieved from remote PC...  proccess it
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function StartAction() AS Long
       Local iRet        AS Long
       Local iWorked     AS Long
       Local CmdInfo     As rCmdExeInfo
       Local CmdRet      AS rCmdReturn
       Local dwWritten   AS Dword
       Local dwRead      AS Dword
       Local hToken      AS Dword
       Local dPriority   AS Dword
       Local zCommand    AS Asciiz * %MAX_PATH
       Local zText       AS Asciiz * %MAX_PATH
       Local zDeskTop    As Asciiz * %MAX_PATH
       Local PI          As PROCESS_INFORMATION
       Local SI          AS STARTUPINFO
       Try
          LogThis "Trying to Recieve command info from remote PC"
          If ReadFile(ghPipe, ByVal VarPtr(CmdInfo), SizeOf(CmdInfo), dwRead, ByVal %Null) = 0 Then 'read pipe from remote PC
             iRet = GetLastError()
             LogThis "Error while reading remote data ReadFile Error:" + Str$(iRet)
             Exit Try
          End If
          LogThis "Received command from PC " + CmdInfo.zPCName + "  cmd=" + CmdInfo.zCommand
          LogThis "Option dFlags=" + Str$(CmdInfo.dFlags)
          SI.cb = SizeOf(SI)
          If IsFalse ConsolePipes(CmdInfo, SI) Then Exit Try           'fill our SI UDT with handles from remote pipes
          zCommand = CmdInfo.zCommand                                    'NOTE: use CMD for internal dos commands.  ex: CMD /C DIR
          If CmdInfo.zStartDir = "" Then 
             CmdInfo.zStartDir = Environ$("SystemRoot") + "\system32"  'if no startup dir was given, then use the system32 as default
          Else
             zCommand = Trim$(CmdInfo.zStartDir,"\") + "\" + CmdInfo.zCommand'NOTE: use cmd for internal dos commands.  ex: CMD /C DIR
          End If
          If (CmdInfo.dFlags And %SET_PRI_IDLE) = %SET_PRI_IDLE Then     'setup processing priorities
             dPriority = dPriority Or %IDLE_PRIORITY_CLASS
          ElseIf (CmdInfo.dFlags And %SET_PRI_HIGH) = %SET_PRI_HIGH Then
             dPriority = dPriority Or %HIGH_PRIORITY_CLASS
          ElseIf (CmdInfo.dFlags And %SET_PRI_REAL) = %SET_PRI_REAL Then
             dPriority = dPriority Or %REALTIME_PRIORITY_CLASS
          Else  'default
             dPriority = dPriority Or %NORMAL_PRIORITY_CLASS
          End If
          If (CmdInfo.dFlags And %SET_SHOW_WIN) <> %SET_SHOW_WIN Then    
             dPriority = dPriority Or %CREATE_NO_WINDOW                  'hide window
          Else                                                           
             zDeskTop = "WinSta0\Default"                                'show window on default desktop
             SI.lpDesktop = VarPtr(zDeskTop)                             
          End If
          If (CmdInfo.dFlags And %SET_SYSTEM) = %SET_SYSTEM Then         'do we use the default SYSTEM account or logon as user?
             LogThis "Now going to CreateProcess for: " + zCommand
             iWorked = CreateProcess("", zCommand, ByVal %NULL, ByVal %NULL, %TRUE, dPriority, ByVal %NULL, CmdInfo.zStartDir, SI, PI)
          Else
             LogThis "Now going to LogonUser " + CmdInfo.zUserID
             zText = "."
             iWorked = LogonUser(CmdInfo.zUserID, zText, CmdInfo.zPassword, %LOGON32_LOGON_INTERACTIVE, %LOGON32_PROVIDER_DEFAULT, hToken)
             If iWorked Then
                zText = ""
                LogThis "Now going to CreateProcess for: " + zCommand
                iWorked = CreateProcessAsUser(hToken, "", zCommand, ByVal %Null, ByVal %Null, %TRUE, dPriority, ByVal %Null, CmdInfo.zStartDir, SI, PI)
             Else
                CmdRet.dError = GetLastError()
                LogThis "LogonUser Error " + Str$(CmdRet.dError)
             End If
          End If
          If iWorked Then
             ghProc = PI.hProcess
             LogThis "CreateProcess worked for " + CmdInfo.zCommand
             If (CmdInfo.dFlags And %SET_NO_WAIT) <> %SET_NO_WAIT Then   'should we wait for process to end?
                LogThis "Now waiting for program to finish."
                WaitForSingleObject(PI.hProcess, %INFINITE)              'wait on this line for process to end
                GetExitCodeProcess(PI.hProcess, CmdRet.dExit)      'get processes exit code
                LogThis "Program finished with exit code " + Str$(CmdRet.dExit)
             End If
          Else
             If CmdRet.dError = 0 Then CmdRet.dError = GetLastError()
             LogThis "CreateProcess failed for " + CmdInfo.zCommand + " err=" + Str$(CmdRet.dError)
          End If
          'now send back the results
          If IsFalse WriteFile(ghPipe, ByVal VarPtr(CmdRet), SizeOf(CmdRet), dwWritten, ByVal %Null) Then LogThis "Failed to send response back to host."
          CloseHandle(si.hStdOutput)
          CloseHandle(si.hStdError)
          CloseHandle(si.hStdInput)
       Catch
          LogThis "Error in StartAction sub.  " + Error$(Err)
       End Try
       DisconnectNamedPipe(ghPipe)                                       'Done, so close com pipe
       CloseHandle(ghPipe)                                               'remove handle
       Call SetEvent(ghStopEvent)                
    End Function
    
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' WaitForCommThread  - Waits for remote comunication.  It stays running until service is shutdown
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function WaitForCommThread(ByVal hName AS Dword) AS Long
       Local SECATTRIB   AS SECURITY_ATTRIBUTES
       Local SECDESC     AS SECURITY_DESCRIPTOR
       Local zText       AS Asciiz * %MAX_PATH
       LogThis "WaitForCommThread started and waiting for client message"
       Do
          Call InitializeSecurityDescriptor(SECDESC, %SECURITY_DESCRIPTOR_REVISION)  'initializes a security descriptor to have no system ACL, no discretionary ACL, no owner, no primary group
          Call SetSecurityDescriptorDacl(SECDESC, %TRUE, ByVal %Null, %TRUE)         'sets information in the discretionary ACL in the security descriptor
          SecAttrib.nLength = SizeOf(SECATTRIB)
          SecAttrib.lpSecurityDescriptor = VarPtr(SecDesc)
          SecAttrib.bInheritHandle = %TRUE
          zText = "\\.\pipe\rCmdCommand"
          ghPipe = CreateNamedPipe(zText, %PIPE_ACCESS_DUPLEX, %PIPE_TYPE_MESSAGE Or %PIPE_WAIT, %PIPE_UNLIMITED_INSTANCES, 0, 0, %MAXDWORD, SecAttrib)
          If ghPipe <> %INVALID_HANDLE_VALUE Then
             ConnectNamedPipe(ghPipe, ByVal %Null)                                   'stays here until remote PC connects
             Call StartAction()
          Else
             LogThis "Timed out waiting for client to talk.  looping..."
          End If
       Loop
    End Function
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' ServiceMain - Main code of service, the entry point of the service  All service processing takes place here
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    SUB ServiceMain(BYVAL dwArgs AS DWORD, BYVAL lpszArgv AS DWORD)
       On Error Resume Next
       Local iRet     AS Long
       Local hComm    AS Dword
       Local iWatch   AS Long
       LogThis "ServiceMain started"
       SS.dwServiceType              = %SERVICE_WIN32_OWN_PROCESS
       SS.dwCurrentState             = %SERVICE_START_PENDING
       SS.dwControlsAccepted         = %SERVICE_ACCEPT_STOP OR %SERVICE_ACCEPT_PAUSE_CONTINUE OR %SERVICE_ACCEPT_SHUTDOWN
       SS.dwWin32ExitCode            = 0
       SS.dwServiceSpecificExitCode  = 0
       SS.dwCheckPoint               = 0
       SS.dwWaitHint                 = 0
       ghServiceStatus = RegisterServiceCtrlHandler(gzServiceName, CodePtr(Handler))
       SS.dwCurrentState             = %SERVICE_START_PENDING
       SetServiceStatus ghServiceStatus, SS
       ghStopEvent = CreateEvent(ByVal %NULL, ByVal %NULL, ByVal %NULL, "HandlerEvent" + Chr$(0))
       SS.dwCurrentState             = %SERVICE_RUNNING
       SetServiceStatus ghServiceStatus, SS
       Thread Create WaitForCommThread(dwArgs) To hComm   'start the main thread
       Thread Close hComm To iRet
       iWatch = Timer
       Do                                                 'stay here until we stop service
          iRet = WaitForSingleObject(ByVal ghStopEvent, 50)
          If iRet = %WAIT_FAILED Or iRet = %WAIT_OBJECT_0 Then Exit Loop
          Sleep 1000
          If Timer > iWatch + 600 Then                    'timeout in 10 minutes  <---  NOTICE!  Might need to adjust this for longer programs
             LogThis "Watchdog killed service because it was older than 10 mins." 
             Exit Loop 
          End If
       Loop
    
       'If (CmdInfo.dFlags And %SET_NO_WAIT) <> %SET_NO_WAIT And ghProc Then call TerminateProcess(ByVal ghProc, ByVal %NULL) 'not sure if I want to kill on exit or not??? hmmmm
    
       Call UnInstall()                                   'done so uninstall this service
       SS.dwCurrentState = %SERVICE_STOP_PENDING          'set status to stopping
       SetServiceStatus ghServiceStatus, SS               'update status
       CloseHandle ghStopEvent                            'delete the event handle
       SS.dwCurrentState = %SERVICE_STOPPED               'set status to stopped
       SetServiceStatus ghServiceStatus, SS               'update status
       LogThis "Leaving ServiceMain"                
    End Sub
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' Handler: Handles all service requests (only setup to handle STOPs for now
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    SUB Handler(BYVAL pControl AS DWORD)
       If pControl = %SERVICE_CONTROL_STOP Then
          SetEvent ghStopEvent
       Else
          SetServiceStatus ghServiceStatus, SS   'update status
       End If
    End Sub
    
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    ' WinMain   -   Main system function  (service exe starts here)
    '==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==~==
    Function WinMain(ByVal hCurInst AS Long, ByVal hPrvInst AS Long, CmdLine AS Asciiz Ptr, ByVal CmdShow AS Long) Export AS Long
       Local iRet     AS Long
       iRet = GetCurrentProcess
       iRet = SetPriorityClass(iRet,%IDLE_PRIORITY_CLASS)
       CoUninitialize                                     'uninitialize COM interfaces (kills the OleThread so service will run without errs when you log off)
       gzServiceName = "rCmdSvc"                          'global var for Service name.
       If Len(Command$) Then
          If InStr(1,UCase$(Command$),"UNINSTALL") Then   'just in case we need to manually uninstall     [img]http://www.powerbasic.com/support/forums/wink.gif[/img]
             Call UnInstall
          ElseIf InStr(1,UCase$(Command$),"START") Then   'or manualy start
             Call StartManually
          Else
             MsgBox "rCmdSvc /UnInstall /Start",,"Usage:"
          End If
       Else                                               'normal execution from service being started
          Try                                             'try to remove old log if found
             Kill "rCmdSvclog.txt"
          Catch                                           'do nothing on error...  not important
          End Try
          Try
             LogThis "Trying to start service"
             STE.lpServiceName = VarPtr(gzServiceName)
             STE.lpServiceProc = CodePtr(ServiceMain)
             If StartServiceCtrlDispatcher(STE) Then
                Function = %TRUE
             Else
                iRet = GetLastError()
                LogThis "Error on StartServiceCtrlDispatcher err#" + Str$(iRet)
             End If
             ExitProcess 0
          Catch
             ExitProcess Err
          End Try
       End If
    End Function

    ------------------
    "I haven't lost my mind... its backed up on tape... I think??"



    [This message has been edited by William Burns (edited October 02, 2003).]
Working...
X