"Run Time error 9 : subscript out of range" when copying - excel

I am trying to copy values from one sheet to another and getting this error
"Run Time error 9 : subscript out of range"
when I run the code below.
Sub updatemultiple()
Workbooks.Open ("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm")
Range("B2:F2").Copy
'Application.DisplayAlerts = False
ActiveWorkbook.Close
ActiveSheet.Paste Destination:=Worksheets(“Sheet1”).Range("B2:F2")
End Sub
Any help? I'm stuck.

There are a number of unanswered parentage questions to your workbook/worksheet/cell references but if you are pasting into the same workbook as the one containing the code then something like this should work.
Sub updatemultiple()
Workbooks.Open ("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm")
Range("B2:F2").Copy Destination:=THISWORKBOOK.Worksheets(“Sheet1”).Range("B2")
'Application.DisplayAlerts = False
ActiveWorkbook.Close False
End Sub
The problems start with how you are determining the currently active worksheet when you open the external workbook. If it was opened, the active worksheet changed then closed byt another person or process then when you opened it it is going to copy the wrong cells. If the destination worksheet is not contained in the workbook containing the code then the destination is an ambiguous reference that depends upon the current state of the application environment.
A short rewrite of your code (with some made-up references thrown in) might be like the following.
Sub updatemultiple()
Dim wb As Workbook
Set wb = Workbooks.Open("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm", ReadOnly:=True)
With wb.Worksheets("Sheet1")
.Range("B2:F2").Copy Destination:=ThisWorkbook.Worksheets(“Sheet1”).Range("B2:F2")
.Close SaveChanges:=False
End With
Set wb = Nothing
End Sub
In short, be as explicit as you can by defining the workbook, worksheet and cell references for each side of every operation. Relying on 'what is active' can only lead to trouble.

If I understood your code correctly, it seems to me that you have the code in one file. When you execute the code, it opens the DESTINATION.xlsm file, copy the cells, close the DESTINATION.xlsm file, then paste the data in the active sheet of the original file that contains the code.
I don't know what data you're copying, but when you copy (manually, not in code) a lot of data from an excel file and then you close it, you get a warning:
There is a large amount of information on the Clipboard. Do you want to be able to paste this information to another program later?
For that reason, I recommend you to paste your copied cells first, and then close the file you're copying from.

Related

How to pause VBA script execution until downloaded .csv file opens to allow copying worksheets between the 2 workbooks?

I have a script which downloads and creates/opens a new Excel .csv workbook of historical stock data from Yahoo Finance. I then copy the contents of worksheet(1) from that .csv workbook and create a new worksheet (at the end) in ThisWorkbook (the one containing my macro/script) and paste the data into it. I have configured my browser (Chrome) to always open .csv file downloads. Here's some sample code:
Sub Macro1()
Dim urlLink As String
Dim csvWorkbook As Workbook
urlLink = "https://query1.finance.yahoo.com/v7/finance/download/AAPL?period1=1592179200&period2=1623715200&interval=1d&events=history&includeAdjustedClose=true"
ActiveWorkbook.FollowHyperlink Address:=urlLink, NewWindow:=True
Set csvWorkbook = ActiveWorkbook
csvWorkbook.Sheets(1).Copy after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
End Sub
urlLink is an example query to download historical data from Yahoo Finance in .csv format. Set your default browser to always open files of type .csv. When ActiveWorkbook.FollowHyperlink is executed, another instance of Excel will open and AAPL.csv will open. This new AAPL.csv will be the active workbook, and then the next line of code will create a new sheet in ThisWorkbook (the workbook containing your script) and copy the contents of AAPL.csv Sheet(1) into it.
My problem is that this works fine when I single step through the code but it fails when run at full speed (i.e. not in debug mode). To clarify, assuming you start with a new blank workbook, if you single step through the above code you'll get a new sheet named AAPL added to your workbook with 253 rows of data (my desired ourcome), but if you run it full speed you'll get a new blank sheet named Sheet1(2) added.
I've figured out that this is because at full speed, the new .csv file isn't getting created/open so when Set csvWorkbook = ActiveWorkbook is run there's still only 1 workbook open (ThisWorkbook) so the next code line just copies Sheet(1) of ThisWorkbook into a new sheet, instead of copying Sheet(1) from AAPL.csv into it (because it doesn't exist when that line of code is run).
Note that the name of the newly created workbook isn't always known, because if there was already an APPL.csv in that download folder it'll name the next one as APPL(1).csv and so forth, so I can't just use the name of the newly created .csv workbook to reference it as I don't know what it'll be.
So I'm asking for a way to emulate single stepping through the code in debug mode when or after ActiveWorkbook.FollowHyperlink is executed. I've tried simply putting a MsgBox after it thinking that requiring the user to click OK to continue execution would help but it didn't, and from what I read I don't think a Wait or similar thing would help either? This is similar to the issue from Pause execution of VBA script until another specific excel workbook opens?
but I don't understand the answer given in that thread and not sure if it really applies? Thanks for any help on this!
You can pass a URL directly to Workbooks.Open():
Dim wb, ws
Set wb = Workbooks.Open("https://query1.finance.yahoo.com/v7/finance/download/AAPL?period1=1592179200&period2=1623715200&interval=1d&events=history&includeAdjustedClose=true")
Set ws = wb.Sheets(1)
'run text to columns if only one column of data
If ws.UsedRange.Columns.Count = 1 Then
ws.Columns(1).TextToColumns Destination:=ws.Range("A1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Comma:=True
End If

Run another macro in another Excel file

Hi so I am building an Excel system to run macro for running several different Macros on other Excel files.
Let's say I have two Excel files: "Parent.xlsx" and "child.xlsx" opened already.
In my Parent.xlsx I am running an VBA script, how can I run a macro called "method1" in my "PERSONAL.XLSB" for my "child.xlsx"
Right now, in my Parent.xlsx, I try to run this macro VBA script:
Workbooks("child.xlsx").Application.Run "PERSONAL.XLSB!method1"
In my PERSONAL.XLSB in Modelue6, I have:
Sub method1()
Dim rTable As Range
Selection.AutoFilter
End sub
Error:
Run-time error'1004':
AutoFilter method of Range class failed
Thank you very much!
You need to qualify and reference the workbook that the code should act on.
Look into the ActiveWorkbook property here: https://learn.microsoft.com/en-us/office/vba/api/excel.application.activeworkbook
Application.Run
In Parent.xlsx (to keep the code, save the file as e.g. Parent.xlsm)
Sub callMethod1()
Dim wb As Workbook
Set wb = Workbooks("child.xlsx")
Application.Run "PERSONAL.XLSB!method1", wb
End Sub
In PERSONAL.XLSB
Sub method1(wb As Workbook)
wb.Activate
If Not Selection Is Nothing Then
If TypeName(Selection) = "Range" Then
If ActiveSheet.AutoFilterMode Then
ActiveSheet.AutoFilterMode = False
End If
Selection.AutoFilter
End If
End If
End Sub
First off, you can't expect macros in a workbook of xlsx format. Make sure that you have eikther xlsm or xlsb as a source. This gets us to the second necessity. The workbook must have been saved to have such a format. You can't call a macro from an open workbook that hasn't been saved (because it doesn't have any format yet).
This is the correct syntax for calling an existing macro in another workbook.
Sub Test_TestCall()
Application.Run "'Personal.xlsb'!Method1"
End Sub
Add arguments to the call in brackets in the sequence required by the called procedure.
You may find it easier, however, to simply set a reference to the other workbook. Here is a step-by-step guide how to set a reference. Once a reference is set you can call all macros in the other project as if they were within the calling workbook. The reference gets saved with the workbook and will still be there when you next open the workbook. The drawback of this (and any other method) is that you can't send a working copy of the calling workbook to third parties unless you send the referenced workbook as well.
The error you get has yet another reason. The Selection is made by the user in the ActiveWorkbook, and since you don't tell Method1 which workbook is active it wouldn't be able to find it, right? However, this problem is best solved by following the most basic of all programming rules: "Avoid the Selection object!" Use the Range object instead. If you absolutely must use the Selection` object then pass it to your procedure as an argument.
Application.Run "'Personal.xlsb'!Method1(Selection)"
' and
Sub method1(MyRange As Range)
Dim rTable As Range
MyRange.AutoFilter
End sub

Excel calculation just on Active Workbook, looking for a workaround

When you have more than one Excel file open, and your VBA/VSTO code calls the Calculate function, or turns on Automatic Calculation, Excel will painfully recalculate all open Workbooks, not just the active Workbook.
This is a well-known and well-reported problem, which has been around for years, but Microsoft doesn't seem interested in fixing it.
Calculate only the active workbook before saving
Microsoft Excel wishlist: Workbook level calculation
Ridiculously, in both VBA and VSTO, Microsoft gives us the ability to:
recalculate a particular Worksheeet
recalculate all open Workbooks
...but there's no option to just recalculate one particular Workbook.
In the financial company I work for, this is a huge issue. Our actuaries have big, bulky Excel files, full of formulae, and when they click on Calculate on one Workbook or we perform a calculate before saving a file, they then have to wait several minutes for all other open Excel files to also get calculated.
There are two ways around this.
You could run some VBA like this:
Application.Calculation = xlManual
For Each sh In ActiveWorkbook.Worksheets
sh.Calculate
Next sh
..but this isn't guaranteed to work. Your "Sheet1" might contain formulae pointing to cells in "Sheet2", but other cells in "Sheet2" might have dependencies back on "Sheet1". So, calculating each Worksheet once might not be enough to perform a full calculation on your Workbook.
Alternatively, you could open each Excel file in a separate instance (by holding down ALT as you open the Excel icon). But then, you lose the full Excel cut'n'pasting functionality, as described here:
Can't fully cut'n'paste between Excel instances
So, my question is... has anyone found a workaround for this issue ?
I just want to recalculate the cells in the Active Excel Workbook.
I wondered if I could add some VBA or VSTO which sets all non-Active Workbooks to "read-only" before I kick off a Calculation on the Active Workbook, thus preventing other Workbooks from being able to be recalculated. But this isn't possible. The "Workbook.ReadOnly" can only be read, not programmatically set.
Or perhaps adding a handler to the Worksheet_Calculate event, which checks if that VBA code which is being run belongs to the Active Workbook, and if not, it aborts attempting to calculate...? But this event actually gets kicked off after that Worksheet has been calculated, so it's too late.
Our company can't be the only one suffering from this issue...
Any advice (other than upgrading to Lotus 1-2-3) ?
This method uses another instance of Excel to avoid multiple workbook calculations. A few lines of the code for using a new instance were taken from this SO question, which deals with a similar topic and may be of interest to you.
You will have to test this for speed in your specific case, since the closing/opening time might not out-weigh the avoided calculations!
Macro steps
Set calculation to manual
Save and exit the desired workbook
Open it in a new instance of Excel
Recalculate
Save, close and re-open in original instance of Excel.
Key point for running this script:
The macro cannot live within the workbook to be recalculated, since it gets closed (twice) during the process. It should be placed in some other "utility" workbook.
Code - see comments for details
Sub CalculateWorkbook(WB As Workbook)
' Store path of given workbook for opening and closing
Dim filepath As String
filepath = WB.FullName
' Turn off calculation before saving
Dim currentCalcBeforeSave As Boolean
currentCalcBeforeSave = Application.CalculateBeforeSave
Application.CalculateBeforeSave = False
' Store current calculation mode / screen update and then set it to manual
Dim currentCalcMode As Integer, currentScreenUpdate As Integer
currentCalcMode = Application.Calculation
currentScreenUpdate = Application.ScreenUpdating
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
' Close and save the given workbook
WB.Close savechanges:=True
' Open a new INSTANCE of Excel - meaning seperate calculation calls
Dim newExcel As Excel.Application
Set newExcel = CreateObject("Excel.Application")
' Could make it visible so that any problems don't leave it hidden in the background
' newExcel.Visible = False
' Set the calculation mode to manual in the new instance sothat the workbook isn't calculated on opening.
' This can't be done without an existing workbook object.
Dim tempWB As Workbook
Set tempWB = newExcel.Workbooks.Add
newExcel.Calculation = xlCalculationManual
newExcel.CalculateBeforeSave = False
' Open the workbook in the new instance of Excel
Dim newWB As Workbook
Set newWB = newExcel.Workbooks.Open(filepath)
' Calculate workbook once
newExcel.Calculate
' Close and save the workbook, tempworkbook and quit new instance
newWB.Close savechanges:=True
tempWB.Close savechanges:=False
newExcel.Quit
' Re-open in the active instance of Excel
Application.Workbooks.Open filepath
' Reset the application parameters
Application.CalculateBeforeSave = currentCalcBeforeSave
Application.ScreenUpdating = currentScreenUpdate
Application.Calculation = currentCalcMode
End Sub
Call the above sub by passing it the workbook object you wish to recalculate (this could be done from a button etc).
This has been tested on a very simple example workbook, and the concept works. However, please test on a copy of your workbook first, since it has not been fully robustness tested, and has no error handling.
The method I use for this in my FastExcel product involves setting WorkSheet.EnableCalculation to false for all the worksheets in all non-activeworkbooks and to True for all the worksheets in the active workbook.
This works but has the disadvantage of making the next calculation a Full calculation of the new active workbook when you change the active workbook: so its a tradeoff.
You can try this out in your situation by downloading the trial version of FastExcel from Download the 15-day full-featured trial of FastExcel V3 build 231.655.789.380
Then use FastExcel Calculation Options and check the Active Workbook checkbox in the Current Calculation Mode settings;
Disclaimer: I own, develop and market the FastExcel product. The FastExcel component that contains the Active Workbook Calculation code is FastExcel V3 Calc

Create a workbook using a macro with no formulas, but still linked back to macro workbook?

So I built this macro workbook that creates a formatted monthly report for me.
How it works is the macro builds the report based on some criteria I define, then it copies the data portion from the worksheet "Slide" and pastes it as values to the next worksheet, "Deliverable", using this code:
Application.Goto (ActiveWorkbook.Sheets("Slide").Range("A1"))
Range("C2", Cells(TableRows, 30)).Select
Selection.Copy
ActiveSheet.Next.Select
ActiveSheet.Paste
then it formats and sorts everything on "Deliverable", and copies it to a new workbook using this:
Sheets("Deliverable").Select
Sheets("Deliverable").Copy
The only issue is when you open up the new workbook that only contains the copy of "Deliverable", it's still linked back to the original macro workbook and you get the security warning. I could care less about this as I know it came from me, but the report is shared outside my organization and I would prefer to avoid questions and/or hassles for my clients.
The new workbook is not macro enabled, and like I said above, contains no formulas, let alone those that reference the original workbook.
What can I do to address this?
OK, the links were being caused by named ranges in the workbook that came over with the copied worksheet. No formulas, just named ranges.
I added this after the worksheet copy and it took care of it.
Dim nm As Name
On Error Resume Next
For Each nm In ActiveWorkbook.Names
nm.Delete
Next
On Error Goto 0

sub terminates but desired workbook not displayed

I have a VBA program that asks for the user to enter a desired range, which is in another opened workbook. If there is an error, i.e. the other workbook is manually activated, but no range is selected, or there is an error, I want the program to display the original macro workbook sheet with an error message. The code below works in Debug mode, but when the VBA program is run, it displays the error correctly but does not display the original macro worksheet. It remains on the sheet that was manually activated by the user. What am I missing?
In the code below, "HMArea" is a Range variable returned by the routine getting user input.
"Macro_Fname" is a string variable for the file name of the original VBA program.
HM_file = FileName(HMArea)
If HM_file = "Macro_Fname" Then
Windows("Macro_Fname").Activate
Sheets("[name of the sheet in Macro_Fname]").Select
Range("D4").Select
MsgBox "ERROR: No data selected"
Exit Sub
End If
Try changing
Windows("Macro_Fname").Activate
to
Workbooks("Macro_Fname").Activate
However, the exact nature of your question is vague. Assuming you are in Workbook A, do you want to select a range in Workbook B (which is open at the same time)?
To refer to macro workbook use ThisWorkbook
When another workbook is opened always assign it to variable so that you can have control over it.
Set wbk = Workbooks.Open("D:\test.xlsx")
When working with multiple workbook always prefix the workbook object. If its ignored it will take active workbook.
`Sheets("[name of the sheet in Macro_Fname]").Select`
Avoid using Select/Acitvate. See here
Range("D4").Select
Once the above issues are fixed your code will run as expected.

Resources