I've been using VBA for many years but in all that time I've never managed to pass a workbook or sheet ByRef, I've had to use string names & set the objects in the partner sub or function... so finally it's time to get some help!
Sub SubOne()
Dim wb as workbook
Dim filepath as string
filepath = "//somepath/somebook.xlsx"
Set wb = application.workbooks.open(filepath)
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb as workbook)
debug.print wb.name
End Sub
Can anyone see why this would trigger a ByRef type mismatch compile error? What am I missing?
Many thanks
You can avoid problems like these by not using the Call Keyword.
Instead of Call SubTwo(wb) use SubTwo wb
Related information: Should I use Call keyword in VB/VBA?
Your original code worked for me but there might have been minor differences
in white space or parentheses that caused the problem. VBA uses parentheses not only to pass arguments to subs / functions but also to evaluate data.
Another point to mention is that ByVal and ByRef should both work for what you are trying to do since Objects are always passed by reference in VBA. ByVal / ByRef only define if the reference itself is passed by value or reference: https://msdn.microsoft.com/en-us/library/ddck1z30.aspx
I want to leave my previous answer here because it is still a valid answer for the posted error message and might help someone in search of a solution.
Previous answer:
My guess is that one of your loaded AddIns is using a Module, ClassModule, Enum etc. named workbook and this causes the compile error.
If you look at your code you will also see that workbook is written lowercase. Usually the VBA Editor would autocorrect this to Workbook unless some other type name is interfering.
To avoid this replace workbook with Excel.Workbook and please try again.
Your code should then look like this:
Sub SubOne()
Dim wb as Excel.Workbook
Dim filepath as string
filepath = "//somepath/somebook.xlsx"
Set wb = application.workbooks.open(filepath)
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb as Excel.Workbook)
debug.print wb.name
End Sub
This works for me:
Sub SubOne()
Dim wb as workbook
Set wb = This.ActiveWorkbook
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb As Workbook)
Debug.Print(wb.Name)
End Sub
I had the same issue. After lot of try and fail, I added option Explicit on top. When I executed the code it showed me that the declaration of the Worksheet variable had a name mismatch with the variable which was passed. That is, shtFSheet was declared and strFSheet was passed. Changing this solved my problem. Hope this helps somebody.
Related
During the process of running a script if I manually remove focus from the Workbook containing the macro I get the error quoted. If I don't click on anything it works without issue. Script errors out only when I'm trying to place selection back into A1 from the "Input" sheet. Break point is on following line:
ThisWorkbook.Sheets("Input").Range("A1").Select
If I debug and place focus back on macro Worksheet the script completes without issue. Previous line:
ThisWorkbook.Sheets("Input").Cells.Delete
runs without error so I'm guessing its the range that is falling out of scope but don't quite understand why as it should be defined by the previous scope notations.
Can someone explain why that line is falling out of scope? Shouldn't the ThisWorkbook define fairly explicitly the Workbook that my code is referencing? Any guidance is greatly appreciated.
It doesn't have anything to do with the reference to ThisWorkbook at all. You simply can't Select a Range in an object that isn't active. Consider this code, which exhibits the same error:
Private Sub OneOhOhFour()
'Executing with Book1.xlsm active and Book2.xlsx open.
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
Debug.Print ThisWorkbook.Name
'Outputs 'Book1.xlsm' to Immediate window.
wb.Sheets("Sheet1").Range("A1").Select 'Error 1004
End Sub
Same thing with Worksheets:
Private Sub OneOhOhFourVTwo()
'Starting on anywhere but Sheet2 gives an error.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
ws.Range("A1").Select 'Error 1004.
End Sub
The simple solution is to Activate the object before you Select within it:
Private Sub NoOneOhOhFour()
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
wb.Activate
wb.Sheets("Sheet1").Range("A1").Select 'No error.
End Sub
Even better is using references and trying to avoid using the Selection and Active* objects entirely.
During the process of running a script if I manually remove focus from the Workbook containing the macro I get the error quoted. If I don't click on anything it works without issue. Script errors out only when I'm trying to place selection back into A1 from the "Input" sheet. Break point is on following line:
ThisWorkbook.Sheets("Input").Range("A1").Select
If I debug and place focus back on macro Worksheet the script completes without issue. Previous line:
ThisWorkbook.Sheets("Input").Cells.Delete
runs without error so I'm guessing its the range that is falling out of scope but don't quite understand why as it should be defined by the previous scope notations.
Can someone explain why that line is falling out of scope? Shouldn't the ThisWorkbook define fairly explicitly the Workbook that my code is referencing? Any guidance is greatly appreciated.
It doesn't have anything to do with the reference to ThisWorkbook at all. You simply can't Select a Range in an object that isn't active. Consider this code, which exhibits the same error:
Private Sub OneOhOhFour()
'Executing with Book1.xlsm active and Book2.xlsx open.
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
Debug.Print ThisWorkbook.Name
'Outputs 'Book1.xlsm' to Immediate window.
wb.Sheets("Sheet1").Range("A1").Select 'Error 1004
End Sub
Same thing with Worksheets:
Private Sub OneOhOhFourVTwo()
'Starting on anywhere but Sheet2 gives an error.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
ws.Range("A1").Select 'Error 1004.
End Sub
The simple solution is to Activate the object before you Select within it:
Private Sub NoOneOhOhFour()
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
wb.Activate
wb.Sheets("Sheet1").Range("A1").Select 'No error.
End Sub
Even better is using references and trying to avoid using the Selection and Active* objects entirely.
I created a userform sub that will allow the user to choose from the open workbook to use as the reference workbook using a combo box. My current script is returning an error that workbooks(wb) is not defined - I assume this is because the variable is defined in two different modules and the combo box is not in the module being called. Ideally would like to use the userform below
Private Sub Go_Click()
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select a workbook name and try again"
Exit Sub
End If
Dim wb As Variant
wb = ComboBox1.List(ComboBox1.ListIndex)
Call GenerateReportUserForm
End Sub
To call this sub:
Sub newMacro()
Dim copyNames As Range, pasteNames As Range, copyAmounts As Range, pasteAmounts As Range, copyDates As Range, pasteDates As Range, _
copyPayment As Range, pastePayment As Range
' For cheques only
Set copyNames = Workbooks(wb).Worksheets(2).Columns("F")
Set copyAmounts = Workbooks(wb).Worksheets(2).Columns("AR")
Set copyDates = Workbooks(wb).Worksheets(2).Columns("AI")
Set copyPayment = Workbooks(wb).Worksheets(2).Columns("AJ")
Set pasteNames = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("A")
Set pasteAmounts = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("C")
Set pasteDates = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("D")
Set pastePayment = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("E")
copyNames.Copy Destination:=pasteNames
copyAmounts.Copy Destination:=pasteAmounts
copyDates.Copy Destination:=pasteDates
copyPayment.Copy Destination:=pastePayment
End sub
Thanks!
wb clearly wants to be a String representing the name of a workbook. Declare it as such.
Dim wbName As String
wbName = ComboBox1.List(ComboBox1.ListIndex)
Side note, name your controls. AvailableFilesBox tells so much more than ComboBox1.
Now, what you want is to pass this variable as an argument; do not use a global variable unless you absolutely MUST.
Call GenerateReportUserForm
Not sure what this is supposed to be doing, but it's not calling newMacro. If you want to make it call newMacro, then change it to this:
NewMacro wbName
Or if you really really want to keep that redundant and distracting Call keyword:
Call NewMacro(wbName)
Note: give that macro a meaningful name that describes what the macro does. "new macro" might be clear now, but not so much once there are 4-5 newer macros in that project - and newMacro2 is NOT an option!
Now, in order to pass wbName as an argument, the procedure needs to declare that it takes a parameter - like this:
Public Sub NewMacro(ByVal wbName As String)
Inside that procedure scope, you don't need to constantly dereference the Workbook object. Do it once, store the object reference into a local variable, then use that variable:
Dim wb As Workbook
Set wb = Workbooks(wbName)
Turns out, that macro doesn't really care for the workbook's name; what it really actually wants is a Workbook object. So, let's make it the caller's responsibility to provide a Workbook.
First we change the signature to take a Workbook parameter:
Public Sub NewMacro(ByVal wb As Workbook)
Then we change the form code to supply it:
Dim wb As Workbook
Set wb = Workbooks(ComboBox1.List(ComboBox1.ListIndex))
NewMacro wb ' or: Call NewMacro(wb)
Remember to always put Option Explicit at the top of every module; Rubberduck can help you find & fix this, and other issues in your code.
I was thinking if its possible to set a Master Workbook in Excel VBA and access it on all procedures and functions?
Since I am pretty new to VBA I don't understand the syntax VBA is using..
Dim MasterWB as Workbook
Set MasterWB = Workbooks.Open("Path to my Workbook")
But if I want to access it in an another Procedure or Function I'll get a Object Definition Error.
I don't want to declare the Object in every Sub or Function.
Thanks a lot!
There are two ways of doing this:
The good practice:
Option Explicit
Sub Test()
Dim MasterWB As Workbook
Set MasterWB = Workbooks.Open("Path")
UseWb MasterWB
End Sub
Sub UseWb(wb As Workbook)
wb.Close
End Sub
The you shouldn't use practice:
Option Explicit
Public MasterWB As Workbook
Sub Test()
Set MasterWB = Workbooks.Open("Path")
UseWb MasterWB
End Sub
Sub UseWb()
wb.Close
End Sub
The first one allows you to pass the Workbook Variable as parameter to other functions in this case you can't use ByVal but other variables like Integeror Long can be used as ByVal instead of (per default) ByRef.
ByVal means you are only passing the value of that variable, so you won't modify it on the function.
ByRef means you pass the reference, so the other function can modify that original variable.
The second way only allows you to use the variable as a reference so there might be changes on it without noticing.
Hope this clears out a bit your question.
During the process of running a script if I manually remove focus from the Workbook containing the macro I get the error quoted. If I don't click on anything it works without issue. Script errors out only when I'm trying to place selection back into A1 from the "Input" sheet. Break point is on following line:
ThisWorkbook.Sheets("Input").Range("A1").Select
If I debug and place focus back on macro Worksheet the script completes without issue. Previous line:
ThisWorkbook.Sheets("Input").Cells.Delete
runs without error so I'm guessing its the range that is falling out of scope but don't quite understand why as it should be defined by the previous scope notations.
Can someone explain why that line is falling out of scope? Shouldn't the ThisWorkbook define fairly explicitly the Workbook that my code is referencing? Any guidance is greatly appreciated.
It doesn't have anything to do with the reference to ThisWorkbook at all. You simply can't Select a Range in an object that isn't active. Consider this code, which exhibits the same error:
Private Sub OneOhOhFour()
'Executing with Book1.xlsm active and Book2.xlsx open.
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
Debug.Print ThisWorkbook.Name
'Outputs 'Book1.xlsm' to Immediate window.
wb.Sheets("Sheet1").Range("A1").Select 'Error 1004
End Sub
Same thing with Worksheets:
Private Sub OneOhOhFourVTwo()
'Starting on anywhere but Sheet2 gives an error.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
ws.Range("A1").Select 'Error 1004.
End Sub
The simple solution is to Activate the object before you Select within it:
Private Sub NoOneOhOhFour()
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
wb.Activate
wb.Sheets("Sheet1").Range("A1").Select 'No error.
End Sub
Even better is using references and trying to avoid using the Selection and Active* objects entirely.