Shell code doesn`t run in XP environment if processing many files - excel

This VBA code I wrote runs in Windows 7 but not in XP.
Public Function WShellRun(command As String)
Dim wShell As Object
Dim waitOnReturn As Boolean: waitOnReturn = True
Dim windowStyle As Integer: windowStyle = 0
Dim errorCode As Integer
Dim wExec, rslt As String
Set wShell = CreateObject("Wscript.Shell")
wShell.CurrentDirectory = ActiveWorkbook.Path
On Error GoTo WinXpMode
errorCode = wShell.Run(command, windowStyle, waitOnReturn)
If errorCode <> 0 Then
MsgBox "SHELL COULDN`T STARTED " & errorCode, vbCritical, "SHELL ERROR"
Exit Function
End If
Set wShell = Nothing
Exit Function
WinXpMode:
Set wExec = wShell.Exec("%ComSpec% /c " & command) ''(1) execute DOS
Do While wExec.Status = 0 ''(2) wait until response
DoEvents
Loop
'rslt = wExec.StdOut.ReadAll ''(3) show result
'MsgBox rslt
Set wExec = Nothing
Set wShell = Nothing
End Function
I thought WSH doesn't run on XP so I wrote a GOTO to which the code would flow when something happened to the script. But when I run it in XP environment, the WinXPMode part and the upper part runs parallel that it freezes the flow of the program (I think).
I have two questions.
Is there any way to upgrade the code to work in both Windows 7 and XP so I don't have to worry about the environment.
Is there any way to check what is wrong with the code or the system?
I found that when I run the .jar for few files (10 or 20 file names), it goes without any problem. After that I tried for many files like 100 file names. Then it showed me an error that it can't output file (can't write file), which doesn't show up in Windows 7.

Related

VBA start stop Onedrive Sync client

Need: Start or stop the Onedrive sync client via VBA
Reason: Running a procedure that looped through files and made some changes caused Excel to crash while Onedrive sync client was running
Couldn't find a more "elegant" way to do it, but it works
Adjust the program files folder if you're using 32bit version
' Credits: https://stackoverflow.com/questions/49652606/wscript-shell-to-run-a-script-with-spaces-in-path-and-arguments-from-vba
Private Sub ManageOnedriveSync(ByVal action As Integer)
Dim shell As Object
Set shell = VBA.CreateObject("WScript.Shell")
Dim waitTillComplete As Boolean: waitTillComplete = False
Dim style As Integer: style = 1
Dim errorcode As Integer
Dim path As String
Dim commandAction As String
Select Case action
Case 1
commandAction = "/shutdown"
End Select
path = Chr(34) & "%programfiles%\Microsoft OneDrive\Onedrive.exe" & Chr(34) & " " & commandAction
errorcode = shell.Run(path, style, waitTillComplete)
End Sub
Shutdown:
ManageOnedriveSync 1
Start:
ManageOnedriveSync 0

open .exe in VBA excel and write in the input window

I have to run an .exe in VBA Excel and write in the input window "in.txt" "out.txt" in order to make the process automatic inside a macro. I tried to use shell but it works asynchrounous and I also don't know how to tell her to write inside the .exe.
I've also tried with SendKeys but apperently it doesen't work.
How could I make the VBA calling my .exe, open it, write inside the command window of the .exe, wait for the output and use it to go on?
thank you in advance
here are two attempts (both failed):
Sub write()
prog = Shell("C:\Users\arancia\Pictures\Camera Roll\axtur\axtur\AXTUR_64.exe", 1)
Application.Run "'AXTUR&EXCEL.xlsm'!inserisci_dati_input"
SendKeys.send "in.txt~", True
SendKeys.send "out.txt~", True
SendKeys "%{F4}", True
End Sub
Sub StartExeWithArgument()
Dim strProgramName As String
Dim strArgument As String
strProgramName = "C:\Users\arancia\Pictures\Camera Roll\axtur\axtur\AXTUR_64.exe"
strArgument = "in.txt~out.txt~"
Call Shell("""" & strProgramName & """ """ & strArgument & """", vbNormalFocus)
End Sub
One solution would be to write a batch file that includes all the parameters and then run the batch.
I have used WshShell (Windows scripting host) to run batch files to do what you want in the past but WshShell does not work on our computers since the Nov 2020 updates. WshShell allows you to wait for the outcome of the external program.
One way I found to go around it is to write a simple text file at the end of the batch and wait for it to show up. This is crude but it works.
In the code below, I write a simple batch file in the folder of the Excel sheet. The last line of the batch writes the content of the folder in a text file. The Do Until loop waits for the text file to show up in 1 second increments. When the code resumes after the loop, the text file is deleted. If you write the command line you would type in cmd instead of "echo Hello World" this should work.
You need to reference the Microsoft Scripting Runtime (scrrun) to use the file system object.
Good Luck!
Public Sub RunBatch()
Dim i As Integer
Dim k As Integer
Dim xlWB As Workbook
Dim fso1 As New FileSystemObject
Dim BatFile As Object
Dim IsDone As Boolean
Dim OutFileName As String
Set xlWB = ThisWorkbook
OutFileName = xlWB.Path & "\" & "HW.bat"
Set BatFile = fso1.CreateTextFile(OutFileName)
BatFile.WriteLine "cd /d " & xlWB.Path
BatFile.WriteLine "echo Hello World"
BatFile.WriteLine "dir > Done.txt"
BatFile.Close
IsDone = False
Call Shell(OutFileName, vbNormalFocus)
Do Until IsDone
If fso1.FileExists(xlWB.Path & "\Done.txt") Then IsDone = True
Application.Wait (Now + TimeValue("00:00:01"))
Loop
fso1.DeleteFile (OutFileName)
End Sub

Permission Denied for VBA Shell Call on Windows 10

We recently upgraded to Windows 10. This VBA code was working in Windows 7 to run a batch script, but now it throws "Run-time error '70': Permission denied.
I've tried several variations of the script, but continue to run into issues. I have Administrator rights on my computer as well. This is on a network drive, but it hasn't been an issue before. Using a cmd prompt, I am able to call this batch file successfully. Any ideas or help you provide would be appreciated.
Sub DailyBatchProcess()
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
Dim waitOnReturn As Boolean: waitOnReturn = True
Dim windowStyle As Integer: windowStyle = 1
wsh.Run Chr(34) & "S:\Data\Daily Items\1 Daily Batch\BAT\Transaction Adjustments.bat" & Chr(34), windowStyle, waitOnReturn
Set wsh = Nothing
End Sub

Running a batch file in a network directory using VBA

I have a .bat file calling from VBA, it is working when I use a local folder as path (example as C:\Users\cthoud01\Desktop\my scripts\scripts).
However, I got an error if I use a path from a network directory (example - H:\scripts). I also tried replacing the path as """H:\scripts\""" but continue to get the same error. I would like to hear from our experts if there is any way around to make this work.
Below is the error message I receive:
VBA Code I am using:
Sub test()
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
Dim waitOnReturn As Boolean: waitOnReturn = True
Dim WindowStyle As Integer: WindowStyle = 1
Dim errorCode As Long
Dim pth As String
errorCode = wsh.Run("cmd.exe /k cd """ & "H:\scripts\" & """ && DeleteMatrix.bat", WindowStyle, waitOnReturn)
If errorCode <> 0 Then
MsgBox "fail, please retry"
End
End If
End Sub
errorCode = wsh.Run("cmd.exe /k cd /d """ & "H:\scripts\" & """ && DeleteMatrix.bat", WindowStyle, waitOnReturn)
Look at the extra /d.
In MSDos each drive had a current directory.
C:\>cd D:\dog
C:\>Copy *.* D:
would copy files into the d:\dog directory. This makes typing easier. There were no mouses or menus.
Windows only has the concept of current directory (and it's per program).
So MSDos batch files run unchanged in CMD you have to tell windows you don't want MSDos behaviour with the CD command by using /d.
Likewise CMD simulates a default drive per directory.
This is what it looks like in the process's environment block
=C:=C:\Users\David Candy\Desktop\Editor\EditorSdi
=D:=d:\~MSSETUP.T

Real time console output from WScript.Shell Exec

I spent most of the day searching for a solution to this, I'm starting to think its maybe not possible for my requirements
My basic setup is to run a vbscript (.vbs) called from an excel vba code. The vba code has to continue on and leave the vbscript running, but will monitor it from time to time using Exec.Status
In the vbscript I'm using WScript.StdOut.WriteLine "whatever" to track/debug it's progress, but as it stands I can only read it's output after the excel vba code is finished what it needs to do.
What I want is to see a real time output to the console from the vbscript
Here's the vba code...
Dim WSH As IWshRuntimeLibrary.WshShell 'Windows Script Host Object Model
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("%COMSPEC% /C CSCRIPT.EXE //nologo " _
& VbsFileDir _
& " " & Arg1 _
& " " & Arg2 _
& " " & Arg3 _
& " " & Arg4)
I have been able to get a real time output by converting from WSH.Exec to WSH.Run, but I do need the access to Exec.Status, which is not available under WSH.Run
UPDATE - 2015-02-06
To clarify further... Using the example '...B.vbs' code provided by #Ekkehard.Horner's answer... The following excel-vba code WILL display a real-time output to the console...
WSH.Run("cscript C:\28353522-B.vbs")
...but the following WILL NOT display anything to the console
WSH.Exec("cscript C:\28353522-B.vbs")
I can't use the .Run() because I use the .Status flag from .Exec()
Also I can't just move the vbscript into the VBA code because the VBA goes on to do other tasks in parallel with the vbscript.
P.s. If anyone can submit an answer explaining why it can't be done, then I will mark that as accepted.
Use .Stdout.ReadLine() until the process has finished and .Stdout.ReadAll() to slurp the rest of the output - as in
28353522-A.vbs
Option Explicit
Const WshFinished = 1
Dim oExc : Set oExc = CreateObject("WScript.Shell").Exec("cscript 28353522-B.vbs")
WScript.Echo "A", "start"
Do While True
If oExc.Status = WshFinished Then
WScript.Echo "A", "WshFinished"
Exit Do
End If
WScript.Sleep 500
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadLine()
Loop
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadAll()
28353522-B.vbs
Option Explicit
Dim i
For i = 1 To 10
WScript.Echo "B", i, "whatever"
WScript.Sleep 100
Next
output:
cscript 28353522-A.vbs
A start
A B 1 whatever
A B 2 whatever
A B 3 whatever
A WshFinished
A B 4 whatever
B 5 whatever
B 6 whatever
B 7 whatever
B 8 whatever
B 9 whatever
B 10 whatever
BTW - How did you get real-time output with .Run()?
Why are you running two files? There is no need.
VBA, being full basic, can write to it's own console.
So put your vbs into VBA (you can cut and paste VBS into VBA and it will work if you put sub/end sub around it). To have the VBS run in VBA put a timer that fires it.
Here's a class module for VBA to create/read/write consoles.
'User global var gconsole
Public Function WriteToConsoles(sOut As String)
If IsConsoleAvailable() = True Then
Dim Result As Long, cWritten As Long
Result = WriteConsole(hConsole, ByVal sOut, Len(sOut), cWritten, ByVal 0&)
End If
End Function
Public Sub ExecuteCommand(Cmd As String, ReturnToPrompt As Boolean)
If IsConsoleAvailable() = True Then
If Len(Cmd) <> 0 Then
If ReturnToPrompt = True Then
Shell Environ$("comspec") & " /k " & Cmd
Else
Shell Environ$("comspec") & " /c " & Cmd
End If
End If
End If
End Sub
Public Sub CreateConsole()
If IsConsoleAvailable() = False Then
If AllocConsole() Then
hConsole = GetStdHandle(STD_OUTPUT_HANDLE)
If hConsole = 0 Then MsgBox "Couldn't allocate STDOUT"
Else
MsgBox "Couldn't allocate console"
End If
End If
End Sub
Public Sub CloseConsole()
If IsConsoleAvailable() = True Then
CloseHandle hConsole
hConsole = 0
FreeConsole
End If
End Sub
Public Function IsConsoleAvailable() As Boolean
If hConsole <> 0 Then
IsConsoleAvailable = True
Else
IsConsoleAvailable = False
End If
End Function
I've come up with an answer to my own question. Though this isn't a preferred solution (as I will explain below), so I'll not be marking as correct, but maybe someone can fix the issues with solution? (if so, post an answer and I'll mark as correct)
First off, +1 to #Ekkehard.Horner's answer for inspiring this solution.
Create the file 'B.vbs' representing my main vbscript to be run.
Option Explicit
Dim i
For i = 1 to 10
Wscript.Echo "B", i, "whatever"
Wscript.Sleep 100
Next
Create the file 'A.vbs' to act as a middle man between the main vbscript and my Excel VBA code
Option Explicit
Dim WSH
Set WSH = CreateObject("WScript.Shell")
WSH.Run "cscript C:\B.vbs", , True
Set WSH = Nothing
Now the excel VBA code...
Option Explicit
Sub Test()
Dim WSH As IWshRuntimeLibrary.WshShell
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("cscript C:\A.vbs")
'Showing that I can still access the Exec.Status
While Exec.Status = WshRunning
Debug.Print "Running"
Wend
'But downside is nothing is avaiable from Stdout
Debug.Print Exec.StdOut.ReadAll
Set Exec = Nothing
Set WSH = Nothing
End Sub
So the Excel VBA calls the 'A.vbs' still using WSH.Exec(), then that will call the 'B.vbs' using WSH.Run(), which opens a second console window which will display the real-time output
Advantages
Excel VBA can still monitor the Exec.Status accurately
Progress of 'B.vbs' can be viewed from real-time console output
Disadvantages (reasons I'm not marking as correct)
The Exec.Terminate() will only terminate the 'A.vbs' (first console window), the 'B.vbs' will remain running
The Exec.StdOut. cannot read the output from 'B.vbs'

Resources