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:
------------------
"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).]
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).]
Comment