VBA Error 424 Object Required on call to sub in Workbook_Open - vba

I am very new to vba. I am getting a 424 Ojbect required error when I open the document. I tried using Call subName but that made no difference.
Please let me know what else I can send or explain.
Code found in ThisWorkbook:
Private Sub Workbook_Open()
Sheet1.Worksheet_Activate//<- Object Required
Sheet2.Worksheet_Activate
Sheet3.Worksheet_Activate
Sheet4.Worksheet_Activate
Sheet5.Worksheet_Activate
Sheet6.Worksheet_Activate
Sheet7.Worksheet_Activate
...
Code found in Sheet1:
'INITIAL SETUP OF THE SHEET
Public Sub Worksheet_Activate()
If Sheet9.Range("B1").Text = "TRUE" Then
'RUN INIT FUNCTIONS HERE
Me.initReqLink
Me.initVersion
Me.initCbApplicaiton
Sheet9.Range("B1").Text = "FALSE"
End If
End Sub
Thank you in advance.

Just change it to Sheet1.Activate and that event will be called.

You shouldn't be invoking event handlers manually, but that's not why you're having that error; by making the method Public you've exposed it onto Sheet1's interface, making these calls perfectly "legal" - so the methods do get invoked, no doubt.
Go to Tools / Options / General / Error Trapping, and set it to Break on Unhandled Errors.
Then you should have a better idea of exactly which statement is blowing up, most likely located somewhere in one of these:
Me.initReqLink
Me.initVersion
Me.initCbApplicaiton
..which you haven't posted.
Side note, Call doesn't make a difference, because it's an obsolete syntax that hasn't had a purpose since the advent of the implicit call syntax way back when. You never need an explicit Call statement. Never.

Related

VBA - Custom Function to close work book - not working

I am trying to write a simple function (in a module FileIO) which would take an instance of a work book, and just close it. This function is invoked from another module Business.
Below is the code snippet.
Public Function CloseExcelFile(wkBook As Workbook)
If (wkBook Is Not Nothing) Then
wkBook.Save
wkBook.Close
End If
End Function
I invoke this method by using the command FileIO.CloseExcelFile(catWorkBook). Variable catWorkBook is the object reference to the workbook I created (in a step before).
When ever I try too invoke the custom function, I am getting the error
object does not support this method or property
The below command closes the work book with no errors.
catWorkBook.Close
But the same does not happen when I use the custom function. What is going wrong here?
You just have your Not in the wrong place. Try it like this:
Public Function CloseExcelFile(wkBook As Workbook)
If Not wkBook Is Nothing Then
wkBook.Save
wkBook.Close
End If
End Function
As braX pointed out, your Not isn't in the right place.
You also don't need a Function here. Change it to a Sub. In fact, you barely need the Sub when you reduce it to one line like this:
Public Sub CloseExcelFile(wkBook As Workbook)
If Not wkBook Is Nothing Then wkBook.Close(SaveChanges:=True)
End Sub
The error was due to the INCORRECT way I was invoking a sub routine. I invoked the sub routine as:-
FileIO.CloseExcelFile(catWorkBook)
The CORRECT way to invoke a function would have been.
FileIO.CloseExcelFile catWorkBook

VBA Excel Call and Run breaking backtracking

I'm making a workbook with a whole lot of different subs, and in an effort to avoid a user accidentally activating a sub that erases the code of a sheet for example, I've tried making all the subs private instead.
My subs can now only be activated by clicking buttons on the worksheet, and it all works as intended. Untill a sub of mine tries to call a private sub in another module of course.
To get around this I used Application.Run rather than Call, which worked and also allows me to call in a variable "NextSub" from the previous sub, which gives me some flexibility that I need, and apparently cant get with the Call.
Eg.
Sub FirstSub()
*Something going on
Application.Run "SecondSub", SomeVariableSub
End sub
Sub SecondSub(Nextsub as String)
If something Then
*Do something
Application.Run NextSub
Else
Application.Run NextSub
I thought that the Application.Run had solved all my problems, but I used to have a line that called an errorhandler, which in turn called a sub. It seems that the program can no longer backtrack to the sub that contained the errorhandler as it could when I used Call.
Does Applciation.Run break this functionality? If yes, can I then use Call with a variable NextSub as I am doing it now? And if I can't use the Call that way, then is all this fixed by adding a On Error GoTo ErrorHandler in the affected subs?
I know that the whole thing about calling Private Subs across modules is probably pretty bad practice, but I was compltely new to this when I started out, and the project is too extensive to fix that without rewriting all of the code.
Instead of making all subs private, either put Option Private Module on top of each module, or add a dummy argument to each routine:
Sub SomeHiddenRoutine(bDummy As Boolean = False)
'Routine can be called as usual using:
SomeHiddenRoutine
End Sub
If I understand you are trying to call a function specified by a string.
The correct way is to use something like this, which allows you to call all the private subs (as long as it is in the same module as the private functions):
Sub CallFunction(FuncName As String)
Select Case FuncName
Case "Func1": Func1
Case "Func2": Func2
Case "Func3": Func3
End Select
End Sub

How do I effectively create controls dynamically in Excel's VBA or How do I use Application.OnTime()?

I am working on a very large VBA project in Excel at my job. We are about 1500 lines of code for just one feature and have about a dozen more features to add. Because of this, I've been trying to break everything down so that I can keep code for each feature in separate places. OOP sucks in VBA... The problem being that these controls MUST have events fired. Of course, some events (like the TextBox_AfterUpdate event) are not available when you dynamically create controls. It's a bit convoluted because of everything that is going on, so I'll break it down the best I can:
I have a class module that represents a tab for a multipage control. When a user clicks on a tab, the Userform calls this class module and THERE I have the controls created dynamically. This way I can keep the code in that class module. I have a sub that I deemed as the "AfterUpdate" sub and put code that I needed to run there. Now the problem is to get that sub to be called at the appropriate time.
So what I did is to set up a Timer of sorts to check and see if the "ActiveControl" is said textbox. If it is not, we can assume that focus has left and we can raise that event. Here's the code I'm using:
An abbreviated version of the tab creation...
Private WithEvents cmbMarketplace As MSForms.ComboBox
Public Sub LoadTab(ByVal oPageTab As Object)
If TabLoaded Then Exit Sub
Set PageTab = oPageTab
Dim tmp As Object
Set tmp = PageTab.Add("Forms.Label.1")
tmp.Top = 6: tmp.Left = 6: tmp.Width = 48
tmp.Caption = "Marketplace:"
Set cmbMarketplace = PageTab.Add("Forms.ComboBox.1", "cmbMarketplace")
' LOAD OTHER CONTROLS '
TabLoaded = True
Start_Timer
End Sub
Then Start_Timer:
Public Sub Start_Timer()
TimerActive = True
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End Sub
And the sub that is to be fired:
Public Sub Timer()
If TimerActive Then
' DO SOME RANDOM THINGS '
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End If
End Sub
Does this seem like a reasonable approach to solving the problem I'm facing? I'm open to suggestions...
That's the first problem. This seems like a lot of work to accomplish this. (I'm working on getting visual studio, but I don't know if that's going to happen)
The above code will work but the "Timer" sub will not get raised at all. I get no errors if I just run the code. Everything is created, everything works as I would hope. However, if I step through the code, I eventually will get the following error:
Cannot run the macro "...xlsm!Timer". The macro may not be available in this workbook or all macros may be disabled.
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module. I tried making it public, same problem. Tried "ClassModule1!Timer" to no avail. I'm at my wits end trying to figure this out. Thinking of having people write ALL this in the Userform or just giving up.
Does anybody have any suggestions on how to effectively break up large chunks of code? And does anybody have a clue why this sub will not run and seemingly cannot be found?
I understand that this is a confusing situation, so if you need more info or code examples or want to know why I have something set up the way I do, let me know.
Thanks!
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module.
There's the problem: a macro cannot be in a class module. The message is entirely correct: VBA cannot see the Timer procedure, because it's not accessible.
A class module is a blueprint for an object, VBA (or any OOP language for that matter) can't do anything with a class module, without an instance of that class - i.e. an object.
Your timer callback needs to be a Public Sub in a standard module, so that it can be called directly as a macro. Public procedures of a class modules are methods, not macros.
Depending on what ' DO SOME RANDOM THINGS ' actually stands for, this may or may not require some restructuring.
1500-liner spaghetti code can be written in any language BTW.

Which SUB is calling this SUB

Does anyone out there know how to do a stack trace in access-vba. I'm trying to do something like:
Public Sub a()
Call c
End Sub
Public Sub b()
Call c
End Sub
Public Sub c()
Debug.Print "Which sub has called me ?"
End Sub
What I want to do in Sub c is to show if that has been called by Sub a or Sub b without passing any arguments. In c I would simply show the stack but I have no idea if this is even possible in VBA - any thoughts ?
You can access the call stack during runtime under the menu View -> Call Stack
Alternatively you can use the keyboard shortcut CTRL+L during runtime.
You can use Mztools Addins which has an option to view the procedure caller.
Download Mztools
The only way you could tell would be to get the return address pointer of the calling subroutine from the stack. VBA wouldn't do that directly, you would have to have written your own library or have a library from someone else who needed to know these things. This might be worthwhile if the calling routines are from a program you can't update, however if this is only coming from your own code, it's far easier to rewrite your code to identify the caller.
The way I do this is by having a ProcedureEnter and ProcedureExit call at the beginning and end of each VBA routine I have. The ProcedureEnter routine has an argument for the Sub/Function name, which gets stored in a global call stack collection. ProcedureExit just pops the last entry off the stack.
So, to get the caller routine name, you would just get the item at the Stack Collection .count - 1
I personally use MZTools to set up a default VBA routine header/footer, so that I just type
Function fnname(arg as whatever) as boolean
End Function
and click the Add Error Handler button in MZTools and it adds the ProcedureEnter and ProcedureExit calls for me. That makes it a lot less cumbersome to add the stack trace code.

How to call another module without returning to the first one after completion?

This is probably the dumbest question I've ever asked here, but it's hard to find answers to things like this.
I have a program with a bunch of modules/subs that each calculate a different variable. They're pretty complex, so I like to keep them separate. Now I want an earlier module to skip to another module based on user input. I thought I could use the call (sub name) method for this, but then the program returns to where the call line was and continues on that module from where it left off.
Example:
Module 1:
Sub NewPracticeSub()
Call otherpracticesub
MsgBox ("We've gone back to this sub... :(")
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub
I don't want it to return to Module 1. What can I do to have it switch control to Module 2 without it then returning to complete Module 1 upon completion of Module 2?
I feel like I just used the most confusing language possible to explain all of this, but thank you for your help anyways!!
Edit: I know I used the words module and sub interchangeably, and I know they're different. I like to keep each sub (which are each very large in my program) in their own modules because it's easier to keep track of them, and easier to explain/demonstrate the application flow to other people.
I think all you're looking for is the command Exit Sub which will make the program leave the subroutine without continuing any further, But the way you usually want to do this is, rather than calling a Sub, rather call a Function that returns a boolean value.
So, for example:
Public Function MyFunc() as Boolean
....
If [good] MyFunc = True
Else MyFunc = False
End Function
Then you could do something along the lines of:
Sub MyCallingSub()
...
If MyFunc = True then Exit Sub
Else ...
End Sub
It just adds in A LOT more felxibility and ability to choose whether you want to continue further in your sub or not.
Hope that makes sense.
Other than using the ugly End statement which I will describe below (and strongly recommend you to avoid), I'm not aware of any way to circumvent the call stack. Even John's response necessarily returns to the calling procedure, and evaluates another statement to determine whether to proceed or end.
This may yield undesirable outcomes, which is why I hesitate to recommend it, in favor of properly structuring your code, loops, etc., with respect to the call stack.
In any case, here is how you can use the End statement within your child subroutines, without needing any sort of public/global variables. This still allows you the flexibility to decide when & where to invoke the End statement, so it need not always be invoked.
Sub NewPracticeSub()
Call otherpracticesub, True
MsgBox ("We've gone back to this sub... :(")
End Sub
Sub otherpracticesub(Optional endAll as Boolean=False)
MsgBox ("We're in the other practice sub!")
If endAll then End '## Only invoke End when True is passed to this subroutine
End Sub
Why I say this method should be avoided, via MSDN:
"Note The End statement stops code execution abruptly, without
invoking the Unload, QueryUnload, or Terminate event, or any other
Visual Basic code. Code you have placed in the Unload, QueryUnload,
and Terminate events of forms and class modules is not executed.
Objects created from class modules are destroyed, files opened using
the Open statement are closed, and memory used by your program is
freed. Object references held by other programs are invalidated.
The End statement provides a way to force your program to halt. For
normal termination of a Visual Basic program, you should unload all
forms. Your program closes as soon as there are no other programs
holding references to objects created from your public class modules
and no code executing."
It will always return but that doesn't mean its a problem. I suggest you use Exit Sub as follows:
Sub NewPracticeSub()
Call otherpracticesub
**Exit Sub**
'Nothing more can execute here so its no longer a worry
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub