Nested macros not looping - vba

I have a project that keeps growing and growing and growing. I'm at the last bit and it isn't looping. I'm not seeing why. I have the elements in their own macros and they perfectly until I try to combine them into a super macro that runs them all. The idea is that the "master" workbook (XLSM) will run its own update, THEN Open all other XLSM files in the same folder and run THEIR Updates which pushes data to other XLSX files. Right now each XLSM file runs a macro that works perfectly by
Sub EndofDay ()
Call step 1
Call Step 2
End Sub
I used the same "Do while" structure for the SuperMacro that opens the other books and calls their "EndofDay". it gets through a couple of the XLSM files then stops with an error on MyFiles = Dir. the following syntax SHOULD run the masters updates and one other code string, THEN open all other XLSM files in the Folder. Why does it suddenly not work at this level when It works on the level under this.
Sub SuperMacroEOD_Trans()
Dim MyFiles As String
Call EndofDayTransfer 'Do this Workbook Transfer first then:
'Step 2: Specify a target folder/directory.
MyFiles = Dir("C:\Users\ME\Desktop\QA VBA Project\*.xlsm")
'Dont try to open this workbook and do anything.
Do While MyFiles <> "" And MyFiles <> "C:\Users\ME\Desktop\QA VBA Project\Update_Master.xlsm"
'Step 3: Open Workbooks one by one
Workbooks.Open "C:\Users\ME\Desktop\QA VBA Project\" & MyFiles
Call EndofDayTransfer 'Call same macro you ran and run in the other workbooks (they contain it).
ActiveWorkbook.Close SaveChanges:=True
'Step 4: Next File in the folder/Directory
Loop
MyFiles = Dir <------------Gets stuck here or editor turns it yellow.
End Sub

There are a few logic mistakes in your code.
1- Here you're telling the code to stop when reached the master file, but what you actually want is to skip the master file. also you are comparing a simple filename to a fullpath name
Do While MyFiles <> "" And MyFiles <> "C:\Users\ME\Desktop\QA VBA Project\Update_Master.xlsm"
2- This should be put inside the loop, that is, before the line Loop
MyFiles = Dir <------------Gets stuck here or editor turns it yellow.
3- you are using ActiveWorkbook which is pretty hazardous. you should use explicit references. Moreover since all the workbooks have the procedure "EndofDayTransfer", including the master WB, it is mandatory to specify explicitly the "scope" so that the appropriate procedure is run (see Application.Run in the code below)
Sub SuperMacroEOD_Trans()
Dim MyFiles As String, wb As Workbook
Call EndofDayTransfer ' Master Workbook Transfer first
MyFiles = Dir("C:\Users\ME\Desktop\QA VBA Project\*.xlsm")
Do While MyFiles <> ""
If MyFiles <> ThisWorkbook.Name Then ' skip the master
' Always get an explicit reference to any file you open and use it
Set wb = Workbooks.Open("C:\Users\ME\Desktop\QA VBA Project\" & MyFiles)
Application.Run "'" & wb.name & "'!EndofDayTransfer" ' <-- specify scope explicitly to disambiguate
wb.Close SaveChanges:=True
End If
MyFiles = Dir ' <-- inside the loop
Loop
End Sub

Related

Suppress "Save As" prompt

I looked this topic up and found some help but the suggestions do not seem to be working.
I am opening a CSV file into EXCEL make some changes and then want to save the results back to the same file name and the CSV format.
I want to do this without the prompt that I am getting to make sure I want to save the file.
We are using a macro enabled excel file to import the data make changes and then save.
This whole process with initiated by a batch file that will open the Excel application and the designated file at regular period of time so that is why we do not want the prompt to stop the process.
Here is the code I am using in VBA to do the work, as well as the other subs I found that were suppose to help me suppress the prompt.
This code is in the TheWorkbook of the file and not a module.
Am I missing something?
code
Sub fixfile()
Const strFileName = "W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv"
Dim wbkS As Workbook
Dim wshS As Worksheet
Dim wshT As Worksheet
Set wshT = Worksheets.Add(After:=Worksheets(Worksheets.Count))
Set wbkS = Workbooks.Open(Filename:=strFileName)
Set wshS = wbkS.Worksheets(1)
wshS.UsedRange.Copy Destination:=wshT.Range("A1")
wbkS.Close SaveChanges:=False
'This is the area of work that we doing to the data
'Through here
Application.DisplayAlerts = False 'IT WORKS TO DISABLE ALERT PROMPT
ActiveWorkbook.SaveAs Filename:= _
"W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv", FileFormat _
:=xlCSVMSDOS, CreateBackup:=False
Application.DisplayAlerts = True 'RESETS DISPLAY ALERTS
Application.Quit
End Sub
Private Sub Workbook_Open()
fixfile
End Sub
Sub CloseandSave()
ActiveWorkbook.Close SaveChanges:=True
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Save
End Sub
The problems in your code are due to the following.
When you call SaveAs on the macro-enabled workbook, you had already appended a worksheet to it:
Set wshT = Worksheets.Add(After:=Worksheets(Worksheets.Count))
and then you're trying to save it as csv, which is a text file with only one worksheet, so Excel complains that you will loose information.
Moreover, you're doing the update to the csv twice: once in the
ActiveWorkbook.SaveAs Filename:= ...
Where, as a result, the current workbook becomes the saved workbook, and then again in the Workbook_BeforeClose. In the latter you dont disable the alerts, but anyway there's no need to save again.
I have come to the conclusion that what you want is to use the macro-enabled wb just as a utility for calculation, and you care only for updating the CSV workbook.
For simplicity, we will disable the alerts for the whole session, because the macro-enabled WB is used as a utility and we dont want the batch job to stop for any reason. However you can do it the traditional way, before and after saving, if you feel more comfortable with it.
' Code module ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
fixCSVFile
' Do the following only if you want the macro-enabled WB to keep
' a copy of the CSV worksheet. but my feeling is you dont want to
' ThisWorkbook.Save
'''''''''''''''''''''
Application.Quit
End Sub
Sub fixCSVFile()
Const strFileName = "W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv"
Dim wbkS As Workbook, wshS As Worksheet, wshT As Worksheet
Set wshT = Worksheets.Add(After:=Worksheets(Worksheets.Count))
Set wbkS = Workbooks.Open(Filename:=strFileName)
Set wshS = wbkS.Worksheets(1)
wshS.UsedRange.Copy Destination:=wshT.Range("A1")
wbkS.Close SaveChanges:=False
'This is the area of work that we doing to the data
' For purpose of testing:
wshT.Range("A1").Value = wshT.Range("A1").Value + 1
' Now we will export back the modified csv
wshT.Move '<- Here we have a temporary workbook copy of the modified csv
With ActiveWorkbook
.SaveAs Filename:=strFileName, FileFormat:=xlCSVMSDOS, CreateBackup:=False
.Close False
End With
End Sub
One more thing, the macro-enabled WB is now such that it closes as soon as it opens so it will be difficult to edit or modify (although there are workarounds). Therefore you should save a back-up copy of it without the Application.Quit, as a testing/maintenance copy. Only the copy that you put in production for the sake of the batch job should have the Application.Quit statement.
Based on the comment in the answers that the reason for opening the file and immediately saving it with no other changes...
So we needed to do what we were doing to get the file edit date to
change but not the actual file.
...this is a complete X-Y problem. If you need to change a file's modified time, just change the file's modified time instead of jumping through all of the opening and re-saving hoops:
Sub UpdateFileModifiedDate()
Const filePath = "W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv"
Dim handle As Integer
handle = FreeFile
Open filePath For Binary As #handle
'Read the first byte.
Dim first As Byte
Get #handle, 1, first
'Write it back
Put #handle, 1, first
Close #handle
End Sub
This will be insanely faster than your current process, will only set the file modified date and time to the time that you run the Sub, and doesn't risk any of the other issues you can run into cycling a CSV file through Excel (date formats and locale issues, truncating decimals, conversions to exponential notation, etc., etc.).
since you're going to consciously overwrite an existing file you can just:
first delete it with a Kill command
then do the SaveAs
so change this code section:
'This is the area of work that we doing to the data
'Through here
Application.DisplayAlerts = False 'IT WORKS TO DISABLE ALERT PROMPT
ActiveWorkbook.SaveAs Filename:= _
"W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv", FileFormat _
:=xlCSVMSDOS, CreateBackup:=False
Application.DisplayAlerts = True 'RESETS DISPLAY ALERTS
Application.Quit
to this:
'This is the area of work that we doing to the data
'Through here
Kill strFileName '<-- delete the old file
ActiveWorkbook.SaveAs Filename:= _
"W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv", FileFormat _
:=xlCSVMSDOS, CreateBackup:=False
Application.Quit
furthermore your code can be refactored by properly handling the ActiveWorkbook and ActiveSheet objects and reduce the variables and code amount, like follows:
Sub fixfile()
Const strFileName = "W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv"
Workbooks.Open(Filename:=strFileName).Worksheets(1).UsedRange.Copy Destination:=Worksheets.Add(After:=Worksheets(Worksheets.Count)).Range("A1") '<--| open 'strFileName', reference and copy its 1st worksheet 'UsedRange' and paste it to a newly added worksheet in the macro workbook. After this statement we're left with the opened workbook as `ActiveWorkbook`
ActiveWorkbook.Close SaveChanges:=False '<--| close `ActiveWorkbook`, i.e. the just opened one. We're left with macro workbook as `ActiveWorkbook` and its newly created worksheet as `ActiveSheet`
'This is the area of work that we doing to the data
'Through here
ActiveSheet.Move '<--| move `ActiveSheet` (i.e. the newly created sheet in macro workbook) to a "new" workbook having that sheet as its only one. We're left with this "new" workbook as `ActiveWorkbook`
Kill strFileName '<--| delete the "old" 'strFileName'
ActiveWorkbook.SaveAs Filename:=strFileName, FileFormat:=xlCSVMSDOS, CreateBackup:=False '<--| save `ActiveWorkbook` (i.e the "new" one) as the new 'strFileName' file
ActiveWorkbook.Close SaveChanges:=False '<--| close `ActiveWorkbook` (i.e the "new" one) without changes (we just "SavedA"s it)
Application.Quit
End Sub
It seems like you are making changes to two files. In addition to the csv file that you are opening, you appear to be adding a sheet to the excel file that is running the VBA code with these lines:
Dim wshT As Worksheet
Set wshT = Worksheets.Add(After:=Worksheets(Worksheets.Count))
So my guess is that you are indeed suppressing the save prompt for the csv file but you are also getting a save prompt for the changes you made to the excel workbook when you attempt to close it. So I think you need to suppress that prompt as well by also turning off DisplayAlerts in the CloseAndSave sub, or wherever the excel workbook is actually being closed.
I don't get why you are copying the CSV sheet into a new sheet in the macro enabled workbook. This is where your problem starts!
You should just be dealing with the data in wshS and saving wbkS instead.
Done, no more problems.
When you call
ActiveWorkbook.SaveAs Filename:= _
"W:\Webshare\Documents Acquired in 2017\Jim Excel\snr-room-schedule.csv",
FileFormat:=xlCSVMSDOS, CreateBackup:=False`
you're renaming the current macro enabled file within excel to the CSV file as far as Excel sees it.
When Application.Quit is called, it is going to call
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Save
End Sub
Which is where the prompt that you are complaining about is happening.
Even if you remove it, after Workbook_BeforeClose is called, Excel is still going to check all the open files' .Saved flag.
Excel will prompt you to save any files where .Saved = False
But if you set ThisWorkbook.Saved = True then Excel will close the file without asking to save.
Solution:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Saved = True
End Sub

Loop to run macros from other workbooks

I would greatly appreciate your help with a macro that I am trying to create.
I have a pathway that looks as follows: K:\XXX\XXX\XXX\Module 1
Module 1 is a folder that contains a bunch of xlsm files named with a number (i.e. 100.xlsm, 110.xlsm, and so forth)
I would like to create a loop that:
Runs the macro in workbook 100.xlsm;
Saves the 100.xlsm (NOT "save as") when the macro is done running;
Closes the saved xlsm, moves on to the next file (i.e.
110.xlsm), and repeats the same steps.
Before running the loop, I would like to create a statement that stores the names of those xlsm files.
The macro below may give you an idea of what I am after. There are indeed several errors.
Sub update()
Dim path As String path = "K:\XXX\XXX\XXX\Module 1"
Dim list() As Integer
List=(100, 110, 137, 140)
For Each n As Integer In list
Application.Run (path & "\" &n.xslm!refresh)
Save WORKBOOK
Close WORKBOOK
Next
End Sub
I think something like the code below will achieve what you are wanting to do.
Note that the code first opens the workbook whose macro you want to run.
You can then run the macro in that opened workbook from your original workbook with the Application.Run() command, e.g.
Application.Run("book1.xlsm!mymacro"), or
result = Application.Run("book1.xlsm!mymacro", "Hello", 20)
The second example calls a macro that requires a string paramater and an integer parameter.
The fuller example below opens some specific workbooks called 100.xlsm, 110.xlsm, etc and then runs a macro in each of them called SayHelloMessage.
I hope this helps.
Sub RunMacrosInOtherWorkbooks()
Dim wbpath As String 'path where the workbooks containing the macros you want to run are saved
Dim wbnames() As String 'array containing names of workbooks whose macros you want to run
Dim wbTarget As Workbook 'current workbook who macro is being run
Dim macroname As String 'name of macro being run
wbpath = "C:\Test"
wbnames() = Split("100.xlsm,110.xlsm,137.xlsm,140.xlsm", ",") 'Just one way of creating the list of workbooks.
macroname = "SayHelloMessage"
Dim i As Integer
Dim result As Variant
For i = 0 To UBound(wbnames)
Set wbTarget = Workbooks.Open(wbpath & "\" & wbnames(i))
result = Application.Run(wbTarget.Name & "!" & macroname)
' result = Application.Run(wbTarget.Name & "!" & macroname, 76) 'calling a subroutine or function with an argument. You need something to catch a return code
wbTarget.Save
wbTarget.Close
Next
End Sub

Working with multiple workbooks and Macros - Calling workbooks? [VBA]

I've got probably what is a simple question but I can't figure out what's going wrong. I'm trying to create one big Macro in excel that will do multiple things to multiple files. The context:
The excel sheet I am running the Macro from is in its own folder. I've set up a little testing folder with this structure:
C:\Users\schris\Desktop\Eprime Testing\
This folder has two folders in it:
\Master Dataset\
In this folder is where the excel file with the macro is
\Eprime Processing\
There are three folders in this folder, named 'Fear', 'Gender', and 'Happy'. In each of those folders is an excel file that I wish to open.
What I wanted to do was create a Sub RunAll that would call various other subs because there are many different things I want the Macro to do, and I wanted to keep it organized.
So:
Sub RunAll()
Call OpenWorkbooks
Call ProcessFear
Call ProcessGender
Call ProcessHappy
End Sub
Here is my OpenWorkbooks code:
Sub OpenWorkbooks()
Dim wb1 As Workbook
Dim wbFear As Workbook
Dim wbGender As Workbook
Dim wbHappy As Workbook
Dim FileToOpen As Variant
Dim FileToOpen2 As Variant
Dim FileToOpen3 As Variant
Dim Sheet As Worksheet
' Must be workbook with the Macros I'm running
Set wb1 = ActiveWorkbook
' Opens Fear
ChDir "C:\Users\schris\Desktop\Eprime Testing\Eprime Processing\Fear"
FileToOpen = Application.GetOpenFilename _
(Title:="Please choose Fear file")
If FileToOpen = False Then
MsgBox "No File Specified.", vbExclamation, "ERROR"
Exit Sub
Else
Set wbFear = Workbooks.Open(fileName:=FileToOpen)
End If
Set wbFear = ActiveWorkbook
' Opens Gender
ChDir "C:\Users\schris\Desktop\Eprime Testing\Eprime Processing\Gender"
FileToOpen2 = Application.GetOpenFilename _
(Title:="Please choose Gender file")
If FileToOpen2 = False Then
MsgBox "No File Specified.", vbExclamation, "ERROR"
Exit Sub
Else
Set wbGender = Workbooks.Open(fileName:=FileToOpen2)
End If
Set wbGender = ActiveWorkbook
' Opens Happy
ChDir "C:\Users\schris\Desktop\Eprime Testing\Eprime Processing\Happy"
FileToOpen3 = Application.GetOpenFilename _
(Title:="Please choose Happy file")
If FileToOpen3 = False Then
MsgBox "No File Specified.", vbExclamation, "ERROR"
Exit Sub
Else
Set wbHappy = Workbooks.Open(fileName:=FileToOpen3)
End If
Set wbHappy = ActiveWorkbook
End Sub
Now I want to be able to reference these three workbooks (wbFear, wbGender, wbHappy) and move seamlessly between them. When testing in Sub OpenWorkbooks(), doing wbFear.Activate would work and process correctly... But when I separated out the tasks of each macro (i.e., OpenWorkbooks now only opens the workbooks, ProcessFear only processes the data in the Fear workbook), I get a Run-time error '91': Object variable or With block variable not set.
I'm assuming this has something to do with the declared workbook names being 'lost' as it switches Subs, but when I put the code from OpenWorkbooks into RunAll and only had ProcessFear run, it still couldn't activate the proper workbooks.
Basically, my question is this:
How can I have one Macro open three workbooks, and declare them all as something that other macros can reference? There are many tasks I need to do, so I really want to have separate Subs for each one, that way I don't get lost in the code.
Thank you for your time!
There are better, more elegant solutions. But this is the simplest and also the easiest for you to implement. Declare 3 global object variables to hold the workbook references. Open the workbooks and assign to these variables in OpenWorkbooks. Use them as needed. Close them and set them to Nothing in a new procedure CloseWorkbooks. (Call is not needed in this context)
Public gwbFear as WorkBook
Public gwbGender as WorkBook
Public gwbHappy as WorkBook
Sub RunAll()
OpenWorkbooks
ProcessFear
ProcessGender
ProcessHappy
CloseWorkbooks
End Sub

Running a macro from a another workbook

I am trying to replace on the line Application.Run "c:\users\navin\test\" with path but it won't work. (Error 1004).
Sub test()
Dim path As String
path = "c:\users\navin\test\"
Workbooks.Open (path & "excel.xlsb")
Application.Run "'c:\users\navin\test\new.xlsb!macro1'"
Workbooks("excel.xlsb").Close SaveChanges:=True
End Sub
Currently you open a workbook called excel.xlsb and then attempt to run a macro in a workbook called new.xslb. You also have two sets of quotes, which is likely to cause problems.
Where you attempt to run the macro, you should reference only the name of the workbook.
Application.Run "excel.xlsb!macro1"

Copy every worksheet from one excel file to another

Having this, probably easy to solve problem, but without any programing skills its hard for me to crack...
I made an excel file with a button, a macro assigned to it.
What it should do :
Open another xls file, for which the user can search on harddrive
copy every sheet from the opened file
Paste it to the original file and close the one it was copied from.
So far I got this:
Sub Importfile()
Dim sFile As String
Dim wb As Workbook
sFile = Application.GetOpenFilename("*.xls,*.xls")
If sFile <> "False" Then
Set wb = Workbooks.Open(sFile)
'Copy and paste code , where I dont know what to do
wb.Close
End If
End Sub
Your example code is right, looking at the recorded macro code should have shown you how to use the worksheet.copy method. Using that you would just have to loop through all the worksheets in your newly opened workbook and copy them to your original workbook.
I've used a For Each, you could also just a plain For or any other sort of loop that you like.
Sub Importfile()
Dim sFile As String
Dim wb As Workbook
Dim ws As Worksheet
sFile = Application.GetOpenFilename("*.xls,*.xls")
If sFile <> "False" Then
Set wb = Workbooks.Open(sFile)
For Each ws In wb.Worksheets
ws.Copy before:=ThisWorkbook.Worksheets(1)
Next ws
wb.Close
End If
End Sub
The macro works fine for me! Please make sure that you have placed the code in the correct location.
In the image below "Book1" is your original sheet (the one you are copying sheets to) the macro code should be inserted into a "module" (the red square) and not any of the ones in the orange square. If you do not have a "module 1" (or any other) you need to insert a new one by looking in the "insert" menu at the top of the vba editor.