Working with multiple workbooks and Macros - Calling workbooks? [VBA] - 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

Related

how can i retrieve a list of currently open workbooks and get VBA to put them in a mulitple choice dialouge box?

Good morning. so in essence i have built a whole bunch of macros that work fine after a lot of work. trouble i have is the macros will always need to perform actions on other workbooks ie: "macro workbook" contains macros that perform actions on "January 2018 data". The reason for this is that the monthly data sheets are always new so i cant store macros on them.
i personally and happy to open a separate workbook, press f11 and run the correct macro. there may be end users in the future however that are not comfortable with this and so i want to employ the use of a button. obviously by implementing a button on the "macro workbook", it applies the macros function to the macro workbook, not the intended one.
In an ideal world i want to press the macro button and be presented with a list of currently opened workbooks that the user can read through and select the correct workbook. it will then select this open workbook and then continue with the macro.
Is this even possible? I'm new to VBA but i have never come across this before. the closest fix i can think of is to ask the user to input the file directory and name in a dialogue box, then use that to select the workbook to continue running the macro, such as:
Sub macro1()
Dim Filepath As String
Filepath = InputBox("Please enter the filename of workbook you wish to run the macro on", "Enter Filepath")
Workbooks(Filepath).Activate
rest of macro can then continue...
End Sub
I would just rather the user be able to click the name of the workbook rather than have to input it, to reduce the chance of them inputting it wrong.
There are so many ways that this could be implemented. Here is a very simple way which might give you at least one idea. In your main workbook with "your macros" … create UserForm with a Combobox and a Button on it … like the following:
Add the following code in the UserForm:
Option Explicit
Private Sub UserForm_Activate()
Dim vWorkbook As Workbook
ComboBox1.Clear
For Each vWorkbook In Workbooks
ComboBox1.AddItem vWorkbook.Name
Next
End Sub
Private Sub CommandButton1_Click()
If ComboBox1.ListIndex <> -1 Then
Call YourMacro(ComboBox1)
End If
End Sub
Private Sub YourMacro(vWorkbookName As String)
MsgBox Workbooks(vWorkbookName).Name
End Sub
Then create a module so that when you bring up the macro dialog box there a macro to load the form.
Option Explicit
Public Sub LoadForm()
UserForm1.Show
End Sub
When the form is activated, the code loads the names of the currently open workbooks into the ComboBox. You can select one. Click the button … and after it checks that a workbook has been selected, it runs an example macro which is mimicking your own macro (which in this example displays the workbook name by referencing the Workbooks object). Because clearly in "your" macros … they should be reference the selected Workbook.
Once again … clearly this is very rudimentary. Its intended to one example of what's possible and might point you in the right direction.
Another method, this will DO_THIS_MACRO on the also open workbook, if there is only 1 other workbook open. If there are 2 or more, will cycle through them asking if you want to DO_THIS_MACRO on that workbook, or go to next, or Cancel.
Sub DoOnOpenWorkbooks()
'Define a Workbook Object
Dim oWb As Workbook, iSh As Worksheet, WkbCount As Double, numa As Integer
'Traverse through each workbook Opened - Using Excel Application Object
WkbCount = 0
For Each oWb In Application.Workbooks
WkbCount = WkbCount + 1
Next oWb
' Do not list PERSONAL and this workbook
WkbCount = WkbCount - 2
Debug.Print "WkbCount: " & WkbCount
'Must be at least 1 other open workbook to continue
If WkbCount = 0 Then Exit Sub
For Each oWb In Application.Workbooks
'Ignore PERSONAL and this workbook
If oWb.Name <> "PERSONAL.XLSB" And oWb.Name <> ActiveWorkbook.Name Then
'If only 1 other workbook, do macro on that
If WkbCount = 1 Then
Workbooks(oWb.Name).Activate
DO_THIS_MACRO
Exit Sub
End If
Workbooks(oWb.Name).Activate
numa = MsgBox("There are " & WkbCount & " workbooks open. Do this on " & oWb.Name & "?", 3, "DO_THIS_MACRO")
If numa = 6 Then
' MsgBox "Yes option pressed!"
Workbooks(oWb.Name).Activate
DO_THIS_MACRO
' Exit or ask to do on all open
Exit Sub
ElseIf numa = 7 Then
'MsgBox "No option pressed!"
Else
' MsgBox "Cancel pressed!"
Exit Sub
End If
End If
Next oWb
End Sub

Pasting into separate workbook must I save the workbook to get the path first

I have a large macro I am amending for my purpose. The writer of the macro was more skilled than me. The macro at present runs formulas on data gathered from websites and other spreadsheets.
All I want to do is to have "Red Flagged" ranges copied and pasted into a the same new workbook. Can this be done without having to save the new work book?
Here was my initial idea:
Sub CreateNewWB()
With Application
.SheetsInNewWorkbook = 1
.Workbooks.Add
.Sheets(1).Name = "Late"
End With
Set ptrToLate = Application.ActiveSheet.FullName
' MsgBox ("This workbook has name" & Application.ActiveWorkbook.Name)
'MsgBox ("This workbook has Full name" & Application.ActiveWorkbook.FullName)
' MsgBox ("This workbook has path name" & Application.ActiveWorkbook.Path)
' MsgBox ("This workbook has Code name" & Application.ActiveWorkbook.CodeName)
End Sub
At the very top of the VBA code I had put
Dim ptrToLate as String
with the intentions of being able to copy and paste using the new workbooks name as a destination, but I get the error: "Object doesnt support this property or method"
1) Is there a way to append to an unsaved workbook?
2)presently the codename of the new workbook is "thisWorkbook" this confuses me because I thought that thisworkbook referred to the workbook the macros itself is written in
Thank you in advance for your help
All you need to do is set the new workbook to an object and then you can reference it without the path. The variable wrkb will now reference your destination new workbook.
Dim wkb As Workbook
'Adding New Workbook
Set wrkb = Workbooks.Add(1) 'this creates anew workbook with only 1 sheet
wrkb.Sheets(1).Name = "Late"
Workbook("Red Flagged.xlsx").Sheets("redflagged").Range("A1:D2").Copy wrkb.Sheets("Late").Range("A1")
To the question in the title: VBA when pasting into separate workbook must I save the workbook to get the path first?
The answer is YES
If you open a new workbook and write this in the VBEditor, you would see that there would an empty MsgBox. If you save it and then run the code again, the path would be shown:
Public Sub TestMe()
MsgBox ThisWorkbook.Path
End Sub

Excel VBA Code to select opened Excel file

I've done lots of search through various website but to no avail.
Previously I create simple VBA to select XLS from automatic-generated-XLS which created by a software as below. This excel does not have any file type at it's end because it was not saved anywhere yet and i'm not planning to save it.
Sub Test()
Windows("All Jobs").Activate
End Sub
Lately, the software generate new file name "All Jobs (0 - XX)" which XX could be any number from 1 to 100. So, the code above does not work anymore. Adding * does not help.
Any suggestion?
Thanks in advance.
Zaiem
You can loop thru the Windows collection to check the caption of each window. Try this:
Private Sub Test()
Dim myWindow As Window
For Each myWindow In Windows
If Left(myWindow.Caption, 8) = "All Jobs" Then myWindow.Activate: Exit Sub
Next myWindow
End Sub
You can loop through all Workbooks in Application (Excel), and if the current workbook's name is Like "All Jobs" & "*" >> this is your desired workbook.
Note: verify that you need to Activate the workbook, usually it is not needed.
Code
Option Explicit
Sub Test()
Dim WB As Workbook
Dim AllJobSWB As Workbook
For Each WB In Application.Workbooks
If WB.Name Like "All Jobs" & "*" Then
Set AllJobSWB = WB ' set the matched workbook
Exit For
End If
Next WB
' if you must activate the workbook, use the line below
AllJobSWB.Activate
End Sub

VBA - Delete all workbooks but the active, then close (and do not save) the active one

i need to find a way to delete all the excel workbooks but the active one after some condition is fulfilled. I am new to VBA, so it is possible that I got here some very basic problem (but I couldn't find similar question here on SO). Here's my code:
Sub kill()
Dim wb As Workbook
Dim A As String
A = 2
If A = 1 Then
MsgBox "Everything is fine"
'The if condition is working just fine
Else
Application.DisplayAlerts = False
For Each wb In Application.Workbooks
If Not (wb Is Application.ActiveWorkbook) Then
Application.DisplayAlerts = False
If wb.Path <> vbNullString Then
wb.ChangeFileAccess vbNormal
kill (wb.FullName)
End If
ThisWorkbook.Close SaveChanges:=False
End If
Next
End If
End Sub
The if condition is working well, but VBA seems to have a difficulties with the kill command, which confuses me since the "killing" part is working perfectly when not put inside of the If Not condition.
Thank you very much for any suggestions you could provide.
Best regards,
Maritn
If you want just to close all workbooks but active one, you could use the code I paste below:
Dim wb As Workbook
For Each wb In Application.Workbooks
If wb.Name <> ActiveWorkbook.Name Then
wb.Save
wb.Close
End If
Next wb
If you want rather to close all workbooks except of the one that have this macro inside, replace ActiveWorkbook with ThisWorkbook. Other thing is to delete all files. This is obviously risky operation, so I would suggest to restrict it to the specific folder. It happened that I have one subroutine of this kind, see:
Sub DeleteFilesFromFolder()
Dim myPath
myFolder = Sheets("Main").Range("B4").Value
Set Fso = CreateObject("Scripting.FileSystemObject")
For Each Filename In fldr.Files
Filename.Delete True ' delete all files
Next
End Sub
The path of the folder to clean is the specific cell, you can put it elsewhere, as you wish. It won't delete open workbook, if you want to avoid error messages, just use On Error Resume Next.
So, my suggestion is first to close all workbooks, then delete closed.

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