How to execute multiple batch files one by one in VBA? [duplicate] - vba

This question already has answers here:
Wait for Shell to finish, then format cells - synchronously execute a command
(8 answers)
Closed 6 years ago.
I have two batch files. The first one is to download files from SFTP. The second is to manipulate some local files. Since I have to wait until the first one is finished, how to hold the program in VBA so the batch files can be executed one by one?
Right now I am using
Call Shell("Bat1.bat")
Call Shell("Bat2.bat")
However, two batch files will be executed at the same time. Bat2.bat will not wait Bat1.bat.
Thanks

First off, drop the Call statement, it's not needed.
Shell "Bat1.bat"
Make Bat1.bat create an empty "marker" file when it's done, e.g. bat1.ready. Then make your VBA code loop until it can find that .ready file:
While Dir("bat1.ready") = vbNullString
DoEvents
Wend
This should keep your UI/host app responsive, while essentially doing nothing until the marker file is up.
Next you delete the marker file and proceed with bat2:
Kill "bat1.ready"
Shell "Bat2.bat"
Ta-da!
The Shell function returns immediately, and returns the created TaskID. If you can't modify the batch file in any way (or ask whoever can modify it if they could do that), then you'll probably need to use some Win32 calls to use that TaskID to determine whether the task has completed, as in the ShellAndWait link that Tim Williams provided in a comment

Related

using environment variables in excel

So I am using this code in excel to read environment parameters on startup:
Dim ExcelArgs As String
Dim arg As String
ExcelArgs = Environ("ExcelArgs")
MsgBox ExcelArgs
If InStr(UCase(ExcelArgs), "CREO") >= 0 Then
Application.DisplayAlerts = False
If Len(ExcelArgs) > Len("CREO") Then
arg = Split(ExcelArgs, ",")(1)
Call Creo.addNewPartToPartslist(arg)
End If
Application.DisplayAlerts = True
End If
and this line in my batch script:
echo "Launch excel"
Set "ExcelArgs=CREO,DXFWITHOUTDRW
"C:\Program Files (x86)\Microsoft Office\OFFICE16\Excel.exe" /r "%APPDATA%\Microsoft\Excel\XLSTART\PERSONAL.XLSB"
exit 0
The problem is that if i run the batch file once, keep excel open change the excelargs to CREO,wqhatever in batch file and rerun batch file the excelargs, dos not get updated!!!
So my theory is that excel either caches out the environment variable or that if it is being used by one instance the batch script can not set it
link with some info about passing arguments to excel:
https://superuser.com/questions/640359/bat-file-to-open-excel-with-parameters-spaces
Usually excel sees if there is a previous instance running and let this instance handle the file opening.
Is this important? Yes, in your case both requests to open the file are handled by the same excel process.
How does it make a difference? Environment variables are not shared. Each process has it own environment block that is initialized when the process is created (can be a customized block or a copy of the environment of the parent process) and once the environment is created for a process, only this process can change its environment.
In your case, when you start excel the new process gets a copy of the environment block of the cmd process. Later, when you change the cmd environment, the already running excel instance sees no changes in environment and, as the new request to open excel is converted to a request to the previous process, there is not a new process with a new copy of the cmd environment with the changes.
The only way I see to make it work is to force excel to start a new process (that will inherit the changes in the cmd instance) instead of reusing the previous one.
But this depends on the excel version. As far as I know, the 2013 version includes an /x switch to force separate process usage. For previous versions, maybe this question, or this one could help you.
Excel is open
Then i start the batch script:
The it does not open it as read only by default, but prompt me instead, not a big issue but a bit annoying, and it also make it impossible to loop through to run the batch several times for different input parameters.
A bit unsure how I should post this, couldnt paste images in comments, and to edit the the original question, which was how to start excel with enviroment variable in new instance (/x did the trick), but now /r does not work, Should I post as new question and refer to this one or can I leave it as an answer?

Either correct VB6/DOS/SQL function or suggest better alternative

I am currently reprogramming code made long ago by a less than skilled programmer. Granted the code works, and has for a number of years but it is not very efficient.
The code in question is in VB6 with SQL calls and it checks a particular directory on the drive (in this example we will use c:\files) and if a file exists, it moves the file to the processing directory loads the parameters for that particular file and processes them accordingly.
Currently the code uses the DIR function in VB6 to identify a file in the appropriate directory. The only problem is that if a number of files exist in the directory it is a crap shoot as to if it will grab the 5kb file and process it in 3 seconds or if it will grab the 500,000kb file and not process any others for the next 10 minutes.
I search many message boards to find some way to have it pick the smallest file and found I could build a complicated array to perform something similar to a sort but I decided to try alternate ideas instead to hopefully reduce processing time involved. Using ancient DOS knowledge I created something that should work, but for some reason is not (hence posting here).
I made a batch file that we will call c:\test.bat which contained the following lines:
delete c:\test.txt
dir /OS /B c:\files\*.txt>c:\test.txt
This deletes a prior existence of test.txt the pipes a directory without headers sorted by file size smallest to largest into c:\test.txt.
I then inserted the following code into the pre-existing code at the beginning:
Shell "c:\test.bat", vbHide
filepath = "c:\test.txt"
Open filepath For Input As #1
Input #1, filegrabber
Close #1
When I step through the code I can see that this works correctly, except now later on in the code I get a
Runtime error 91 Object variable or with block variable not set
in regard to assigning a FileSystemObject. Am I correct in guessing that FSO and Shell do not work well together? Also if you can suggest a better alternative to getting the smallest file from a existing directory suggestions are appreciated.
No need for sorting.
Just use Dir() to cruise through the directory. Before the loop set a Smallest Long variable to &H7FFFFFFF then inside the loop test each returned file name using the FileLen() function.
If FileLen() returns a value less than Smallest assign that size to Smallest and assign that file name to SmallestFile a String variable.
Upon loop exit if Smallest = &H7FFFFFFF there were no files, otherwise SmallestFile has your file name.
Seems incredibly simple, what am I missing?
Another approach is to use the FileSystemObject's Files collection. Just iterate the files collection for a given folder and evaluate each File object's Size property. So long as you don't have a million files in a folder or something, performance should be fine.

vb.net MS Word crash cleanup

I have a program that takes a template .docx file and populates it with data, saving a copy afterwards. The program itself works fine and I got a nice try..catch in main sub in case something fails, so the file is closed regardless.
The problem is that if it crashes completely, i.e. is forced to close (or manually force closed if it hangs), it will keep the process running with the opened template, so next time it;s launched, you'll get the read only error when trying to open it.
So the question: Is there a way to clean up afterwards, without having to end process via task manager? Or maybe a way of opening it without locking it out? Making a temp copy maybe?
Fixed with this:
Sub KillUnusedWordProcess()
Dim oXlProcess As Process() = Process.GetProcessesByName("Winword")
For Each oXLP As Process In oXlProcess
If Len(oXLP.MainWindowTitle) = 0 Then
oXLP.Kill()
End If
Next
End Sub

Running SAS Stored Process in Excel fails after two runs

I hope this is an appropriate place to ask this question.
I have recently built a data analysis tool in Excel that works by submitting inputs to a SAS Stored Process (as an 'input stream'), running the processes and displaying the results in Excel.
I also use some code to check for and remove all active stored processes from the workbook before running the process again.
This runs successfuly the first 2 times, but fails on the third attempt. It always fails on the third attempt and I can't figure out why.
Is there some kind of memory allocation for Excel VBA that's exhausted by this stage? Or some other buffer that's maxed out? I've stepped-in to every line of the VBA code and it appears to hang (on the third run) at the following line:
SAS.InsertStoredProcess processLoc, _
outputSheet.Range("A1"), , , inputStream
Code used to initiate SAS Add-in for Microsoft Office:
Dim SAS As SASExcelAddIn
Set SAS = Application.COMAddIns.Item("SAS.ExcelAddIn").Object
Code used to delete stored processes from target output sheet:
Dim Processes As SASStoredProcesses
Set Processes = SAS.GetStoredProcesses(outputSheet)
Dim i As Integer
For i = 1 To Processes.Count
' MsgBox Processes.Item(i).DisplayName
Processes.Item(i).Delete
Next i
Code used to insert and run stored process:
Dim inputStream As SASRanges
Set inputStream = New SASRanges
inputStream.Add "Prompts", inputSheet.Range("DrillDown_Input")
SAS.InsertStoredProcess processLoc, _
outputSheet.Range("A1"), , , inputStream
Cheers
On reflection, my theory here would be that you are hitting the limit of multibridge connections. Each multibridge connection represents a port, and the more ports you have the more parallel connections are enabled. By default there are three, perhaps you have two, or you are kicking off another STP at the same time?
This would explain the behaviour. I had a spreadsheet that called STPs and it would always fail on the fourth call because the first three were running. You can get around this by either a) increasing the number of multibridge connections or b) chaining your processes so they run sequentially.
I don't know if this'll be useful, but I had a similar problem running a DLL written in C++ through VBA. The problem occurred because the function in the DLL returned a double value, which I didn't need and so the code in VBA did
Call SomeProcessFromDLL()
But the process was returning a double floating point value which was 'filling up' some buffer memory in VBA and VBA has a limited buffer (I think it gave up at 8 tries). So the solution for me was
Dim TempToDiscard as Double
TempToDiscard = SomeProcessFromDLL()
Maybe looking at the documentation of the process being called would help here, especially if it's returning some value to be discarded anyway, like a
Return 0;
I never liked using the IOM in VBA, mainly due to issues with references and having to do client installs when rolling out applications. Last year I found a MUCH better way to connect Excel and SAS - using the Stored Process web application. Simply set up your server side SAS process with streaming output, and pass your inputs via an Excel Web query. No client installs, no worries about SAS version upgrades, hardly any code - am surprised it's not used more often!
See: http://rawsas.blogspot.co.uk/2016/11/sas-as-service-easy-way-to-get-sas-into.html

VBA Word 2010 Paste Error / No Wait Command

I have a project that takes several documents as inputs, does some processing on them, and creates several new documents at the end. I am currently running into problems with pasting content from one Word document into another. The following code snippet seemed relevant:
Set refOrigin = FindReference(OriginDoc)
Set refDest = PasteDoc.Range(PasteDoc.Content.Start, PasteDoc.Content.End)
refDest.Collapse wdCollapseEnd
refOrigin.Copy
refDest.Paste
When running this code, I will occasionally get Run-time error 4198, Command Failed at the paste line in the code. However, when I go into the debugger, I can see that both refDest and refOrigin are valid ranges. Furthermore, when I step through the code line-by-line, it works. However, I can tell that in the instance where it failed, it inserted an embedded Word document already.
I've done some research on the issues and I believe that there is some type of problem of the code running to fast for the clipboard to keep up with it sometimes. This makes sense to me because when I run the macro from a document on a network drive, it runs without a hitch.
I thought that I would be able to simply add a wait command with Application.Wait, but it turns out that Word 2010 doesn't support this command; it's only in Excel.
Does anyone have ideas as to the root of this problem, possible solutions, or any way to give Word 2010 a wait command? Thanks.
For completeness, the following code mimics the Excel `Application.Wait' method from this question.
Dim tmpStart
tmpStart = Timer
Do
DoEvents
Loop While (tmpStart + 1) > Timer