DCL Programming - Remote connections - openvms

First, I am a total newb, and shouldn't be allowed around a keyboard. That said, I am trying to write a DCL .COM file that will allow a user to connect to a remote device by selecting it from a list.
All I want to do is allow them to pick a device and connect, and then when they disconnect from the device, be back where they started. It keeps dumping me out after I terminate the remote connection.
The operating system is OpenVMS. Here is the code (where xxx.xxx.xxx.xxx will be an IP address of the remote system).
Any help will be greatly appreciated!
$! MRV Terminal Server Connection Menu
$!
$ ON ERROR THEN $ LOGOUT
$ GOMENU:
$!
$ CLS:==SET TERM/WIDTH=80
$ WT:==WRITE SYS$OUTPUT
$!
$ CLS
$ WT " MRV Terminal Server Connection Menu "
$ WT " "
$ WT " 1 MRV 1"
$ WT " 2 MRV 2"
$ WT " 3 MRV 3"
$ WT " 4 MRV 4"
$ WT " 5 MRV 5"
$ WT " 6 MRV 6"
$ WT " 7 MRV 7"
$ WT " 8 MRV 8"
$ WT " 9 MRV 9"
$ WT " 10 MRV 10"
$ WT " 11 MRV 11"
$ WT " 12 MRV 12"
$ WT " 13 MRV 13"
$ WT " 14 MRV 14"
$ WT " "
$ WT " "
$ WT " "
$ WT " "
$ WT " "
$ WT " X EXIT"
$ WT " "
$ INQUIRE ANS "Select the MRV you wish to connect to:"
$!
$!
$ IF ANS .EQS. "X" then goto goodbye
$!
$!
$ IF ANS .EQS. "1" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "2" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "3" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "4" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "5" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "6" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "7" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "8" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "9" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "10" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "11" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "12" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ IF ANS .EQS. "13" then SSH "InReach#xxx.xxx.xxx.xxx"
$!
$ GOODBYE:
$!EXIT

Don't muck with terminal settings!
INQUIRE is bad, more often than not, but good enough for now.
Use TYPE for large chunks of constant text
Table lookup, or associative arrays are so much cleaner than long IF THEN ELSES
Use F$TYPE to see if a symbol is a STRING or INTEGER or NOTHING-AT-ALL
Check this out for some ideas:
$ ! MRV Terminal Server Connection Menu
$ !
$ CLS :== TYPE/PAGE NL: ! Please don't muck with my screen setting as in: SET TERMINAL/WIDTH=80
$ WT :== WRITE SYS$OUTPUT
$ ANS_1 = "aap.xxx.xxx.xxx"
$ ANS_2 = "noot.xxx.xxx.xxx"
$ ANS_14 = "mies.xxx.xxx.xxx"
$ !
$ Menu:
$ !
$ ! If SSH (or anything else) causes an error, go back to the menu.
$ ! It is placed here because it needs to be set again after each error.
$ on error then $ goto Menu
$ !
$ type/page sys$input:
MRV Terminal Server Connection Men
1 MRV 1
2 MRV 2
:
14 MRV 14
X EXIT"
$ !
$ INQUIRE ANS "Select the MRV you wish to connect to:"
$ !
$ if ANS .eqs. "X" then exit
$ if f$type(ANS_'ANS') .eqs. ""
$ then
$ ! Handle any unexpected input.
$ WT "Beg your pardon? ''ANS' ? " ! Immediately followed by clearing the screen. Sigh.
$ WAIT 0:0:3
$ !
$ ! We did what they wanted. How else may we serve?
$ goto Menu
$ endif
$ command = "SSH """ + ANS_'ANS + """"
$ write sys$output "Going to execute : " + command
$
$! 'command ! Remove Comment for real action

This may be more to your liking:
$ ! MRV Terminal Server Connection Menu
$ !
$ CLS :== SET TERMINAL/WIDTH=80
$ WT :== WRITE SYS$OUTPUT
$ !
$ Menu:
$ !
$ ! If SSH (or anything else) causes an error, go back to the menu.
$ ! It is placed here because it needs to be set again after each error.
$ on error then $ goto Menu
$ !
$ CLS
$ WT " MRV Terminal Server Connection Menu "
$ WT " "
$ WT " 1 MRV 1"
$ WT " 2 MRV 2"
$ WT " 3 MRV 3"
$ WT " 4 MRV 4"
$ WT " 5 MRV 5"
$ WT " 6 MRV 6"
$ WT " 7 MRV 7"
$ WT " 8 MRV 8"
$ WT " 9 MRV 9"
$ WT " 10 MRV 10"
$ WT " 11 MRV 11"
$ WT " 12 MRV 12"
$ WT " 13 MRV 13"
$ WT " 14 MRV 14"
$ WT " "
$ WT " "
$ WT " "
$ WT " "
$ WT " "
$ WT " X EXIT"
$ WT " "
$ !
$ INQUIRE ANS "Select the MRV you wish to connect to:"
$ !
$ if Ans .eqs. "X"
$ then
$ goto Houseclean
$ else if Ans .eqs. "1"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "2"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "3"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "4"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "5"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "6"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "7"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "8"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "9"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "10"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "11"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "12"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else if Ans .eqs. "13"
$ then
$ SSH "InReach#xxx.xxx.xxx.xxx"
$ else
$ ! Handle any unexpected input.
$ WT "Beg your pardon?" ! Immediately followed by clearing the screen. Sigh.
$ endif
$ !
$ ! We did what they wanted. How else may we serve?
$ goto Menu
$ !
$ Houseclean:
$ ! Nothing else to do.
$ exit ! Or perhaps you prefer $ logout

Related

vba: execute shell after finishing previous shell

How to call the second shell in this case after pressing pause?
Shell "cmd /c cd %tmp% && echo hello > tmpfile && pause", 1
Shell "cmd /c cd %tmp% && echo hello > tmpfile && pause", 1
and in this case
Shell "cmd /c cd %tmp% && echo hello > tmpfile", 0
Shell "cmd /c cd %tmp% && echo hello > tmpfile", 0
The Shell command you're using is asynchronous meaning it can run more than one process at a time, so in the case of your two Shell statements in a row, both will be executed simultaneously.
An alternative way is to instead use the Run command of Windows Script Host (WScript.Shell) since it has more options including one to wait for execution of the program to finish before it continues.
Sub ShellWait(fName As String, Optional showWindow As Boolean = True)
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
wsh.Run fName, -showWindow, True
End Sub
You can also hide the window completely by specifying False as the second parameter. (Caution with that option if user input is required!)
For example:
Sub demo()
ShellWait "x:\test.bat"
Beep
MsgBox "Finished running!"
End Sub
More Information:
Chip Pearson : Shell and Wait
Stack Overflow : Wait for shell command to complete
Stack Overflow : Wait for Shell to finish, then format cells
DevGuru : WSH » wshshell » Run

Execute command with Excel macro and close cmd window

Here is what I'm trying right now:
Sub del_BJSFM_files()
Call Shell("cmd.exe /S /K" & "cd /d C:\UTAS-SA && del /f/s/q BJSFM > nul", vbNormalFocus)
End Sub
The problem is that the command window stays open.
I tried removing the "/K" but then the command doesn't execute properly.
Here is code for it
Sub del_BJSFM_files()
Call Shell("cmd.exe /S /c" & "cd /d C:\UTAS-SA && del /f/s/q BJSFM > nul", vbNormalFocus)
End Sub
I replaced /k with /c and now it closes the window.
/C - Run Command and then terminate
/K - Run Command and then return to the CMD prompt. This is useful for testing, to examine variables
Source - https://ss64.com/nt/cmd.html

Running batch file from Excel VBA

I have a batch file (Windows command line) that creates a VBS file, runs it, and that in turn re-launches the cmd.exe program in elevated mode so that I can copy a file to the System32 folder.
If I run this batch file manually, all goes according to plan, but if I run it from a VBA Excel macro the batch file gets stuck in an infinite loop, even after the UAC prompt has appeared and I have allowed elevated access. Am I calling the batch file incorrectly from VBA?
I'm fine with VBA, OKish with windows CMD, but haven't often used the two together.
I've tried the following statements in my macro, all 3 have the same result:
Shell Environ("USERPROFILE") & "\Desktop\TEMP.bat"
CreateObject("WScript.Shell").Run "CMD /C %USERPROFILE%\Desktop\TEMP.bat", 0, False
Debug.Print CreateObject("WScript.Shell").Exec("CMD /C %USERPROFILE%\Desktop\TEMP.bat").StdOut.ReadAll
For what it's worth, this is the Batch file code:
#ECHO OFF
:----------------------
>NUL 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
IF '%ERRORLEVEL%' NEQ '0' (
GOTO UACPrompt
) ELSE ( GOTO gotAdmin )
:UACPrompt
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%TEMP%\getadmin.vbs"
ECHO UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%TEMP%\getadmin.vbs"
"%TEMP%\getadmin.vbs"
EXIT /B
:gotAdmin
IF EXIST "%temp%\getadmin.vbs" ( DEL "%TEMP%\getadmin.vbs" )
PUSHD "%CD%"
CD /D "%~dp0"
:----------------------
:: < REST OF SCRIPT HERE - NOT RELEVANT TO QUESTION >
And this is what's in the VBS file:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "C:\Users\SO\Desktop\TEMP.bat", "", "", "runas", 1
Any pointers/suggestions welcome. Thanks in advance.
I had faced the same problem, and below code solved it. Howerver, the problem is Shell doesn't have bWaitOnReturn option.
Dim Foldername As String
Foldername = "C:\WorkspaceRFT\RMS\command.bat"
Shell "C:\WINDOWS\explorer.exe """ & Foldername & "", vbNormalFocus

Sending multiple commands from Visual Basic to cmd

So I have batch file that contains series of commands. (about 10) -
I've decided that I would like to create simple gui for this tool - simply one button that would execute commands one after another (when command A is finished command B starts)
Thing is that I do not want to use SendKey method, I am looking for a way that would not even show command prompt window. Is there any way to achieve this?
I am obviously beginner, so any help is welcome :).
Thank you very much!
This is part of the batch file that I somehow need to "translate" to VBA code.
wevtutil epl Application %temp%\Sysinfo\AppLog.evtx
wevtutil epl System %temp%\Sysinfo\SystemLog.evtx
takeown /f %temp%\Sysinfo /r /d y
icacls %temp%\Sysinfo /grant administrators:F /T
set FILETOZIP=%temp%\Sysinfo
set TEMPDIR=%temp%\Zip
rd /s /q %TEMPDIR%
mkdir %TEMPDIR%
copy %FILETOZIP% %TEMPDIR%
echo Set objArgs = WScript.Arguments > _zipIt.vbs
echo InputFolder = objArgs(0) >> _zipIt.vbs
echo ZipFile = objArgs(1) >> _zipIt.vbs
echo CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" ^& Chr(5) ^& Chr(6) ^& String(18, vbNullChar) >> _zipIt.vbs
echo Set objShell = CreateObject("Shell.Application") >> _zipIt.vbs
echo Set source = objShell.NameSpace(InputFolder).Items >> _zipIt.vbs
echo objShell.NameSpace(ZipFile).CopyHere(source) >> _zipIt.vbs
echo wScript.Sleep 2000 >> _zipIt.vbs
CScript _zipIt.vbs %TEMPDIR% %userprofile%\Desktop\Systeminfo.zip
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False
Above runs anything passed on the command line in a hidden window.
& seperates commands on one line
cmd /c dir&time /t%date /t%type c:\windows\win.ini&echo hello

How to capture the PID of a process when launching it from command line?

Is there a way to do this purely in a .bat file?
The purpose is to launch iexplore.exe, then kill just that instance when it's finished.
Here's what I use:
#echo off
rem there is a tab in the file at the end of the line below
set tab=
set cmd=javaw -jar lib\MyProg.jar
set dir=%~dp0
echo Starting MyProg
set pid=notfound
for /F "usebackq tokens=1,2 delims=;=%tab% " %%i in (
`wmic process call create "%cmd%"^, "%dir%"`
) do (
if /I %%i EQU ProcessId (
set pid=%%j
)
)
echo %pid% > MyProg.pid
The directory is set to the directory that the cmd file is located in. Change dir to alter that. Modify cmd to change which command is run.
If you want a stop.cmd that kills it, it would look like this
#echo off
for /F %%i in (%~dsp0MyProg.pid) do taskkill /F /PID %%i
del %~dsp0MyProg.pid
you can use vbscript, here's an example creating notepad, then terminating it using its pid
strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
Set objProcess = GetObject("winmgmts:root\cimv2:Win32_Process")
errReturn = objProcess.Create("notepad.exe", null, objConfig, PID)
If errReturn = 0 Then
WScript.Echo "Process ID is: " & PID
End If
WScript.Echo "Ready to kill process: " & PID & "? [Y|y]"
Do While Not WScript.StdIn.AtEndOfLine
strInput = strInput & WScript.StdIn.Read(1)
Loop
If LCase(strInput) = "y" Then
WScript.Echo "Select * from Win32_Process Where ProcessId = '" & PID & "'"
Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = '" & PID & "'")
For Each objProcess in colProcessList
objProcess.Terminate()
Next
End If
save as myscript.vbs and on command line
c:\test> cscript /nologo myscript.vbs
A slight variation on the answer provided by #kybernetikos since it has a parsing issue. Note the line if %%j gr 0 (
#echo off
rem there is a tab in the file at the end of the line below
set tab=
set cmd=javaw -jar lib\MyProg.jar
set dir=%~dp0
echo Starting MyProg
set pid=notfound
for /F "usebackq tokens=1,2 delims=;=%tab% " %%i in (
`wmic process call create "%cmd%"^, "%dir%"`
) do (
if %%j gtr 0 (
set pid=%%j
)
)
echo %pid% > MyProg.pid
Most often you do know what task you start - in this case, which page iexplorer shall show.
So how about
taskkill /FI "Windowtitle eq *yourpagetitle*"
It will kill all instances of something showing your page title, but with a specific title most often there should be exactly one.
Tom
Ummm, TaskList & TaskKill?!
For some reason your approach of getting process id did not work for me, but since I'm expert in batches, I've coded my own approach, attaching here:
#echo off
call:AsyncCmd
rem call:AsyncCmd "echo hello world"
rem call:AsyncCmd "call build.bat"
exit /b
rem ------------------------------------------------------------------------------------------
rem Starts asynchronous command execution
rem %1 is command, if empty - only aborts existing build.
rem ------------------------------------------------------------------------------------------
:AsyncCmd
if exist %~dp0SetupBuild_Completed.txt (
del /f %~dp0SetupBuild_Pid.txt >nul 2>&1
del /f %~dp0SetupBuild_Completed.txt >nul 2>&1
)
if not exist %~dp0SetupBuild_Pid.txt goto lStartProc
rem --------------------------------------------------
rem Abort build process
rem --------------------------------------------------
set /p pid=<%~dp0SetupBuild_Pid.txt
echo Cancelling setup build process, process id %pid%
pskill -t %pid%
del /f %~dp0SetupBuild_Pid.txt >nul 2>&1
:lStartProc
if "%~1" == "" exit /b 0
rem --------------------------------------------------
rem Starts asyncronous build process
rem --------------------------------------------------
set dir=%~dp0
set dir=%dir:~0,-1%
for /f "tokens=2 delims==; " %%a in ('wmic process call create "cmd /c mincon.exe && %~1 && echo 1>%~dp0SetupBuild_Completed.txt"^, "%dir%" ^| findstr /r "ProcessId"') do set pid=%%a
echo Setup build started, process id: %pid%
echo %pid%>%~dp0SetupBuild_Pid.txt
exit /b 0
PowerShell can be used for this:
powershell -executionPolicy bypass -command "& {$process = start-process $args[0] -passthru -argumentlist $args[1..($args.length-1)]; exit $process.id}" notepad test.txt
echo Process ID of new process: %errorlevel%
I think you can't do that with simple command line utilities, as IE actually spawns child processes for each tab, i.e. if IE is not yet running you would get one parent IE process and a child process for the tab, and if IE is already running you would simply get a single child process.
It will be even quite tricky when you write your own tool to kill IE because when you kill a child (tab) process, IE will automatically recover this tab.
See also this related question: How to obtain process of newly created IE8 window? (though there is no good answer there).