How do I explain the 'expected variable or procedure, not module' error in this instance?
Code:
Routine -
Sub WTS_9_Click()
Call Module1
End Sub
Module -
Sub Rec_9_Click()
The error came about because I learnt a better way to structure code by calling in sub-routines through modules. I have activated the Solver, carefully renamed Modules - set variables, functions and other objects are not interfering - and, yet, it remains stuck. Ideas to help explain the jam would be appreciated.
You dont Call a module, you Call the routine, and the word Call is optional.
You probably want this:
Sub WTS_9_Click()
Rec_9_Click
End Sub
Within you module you need to declare (public) routines, i.e.
Module (named Test)
Public Sub TestMessage()
MsgBox "Test"
End Sub
Then in your code (i.e. events) you can call this routine within the module:
Sub Button_Click()
call TestMessage
call Test.TestMessage ' you can skip the module name if routine name is unique
End Sub
Related
I'm learning error handling and I think I'm getting the hang of it. But I've come across an interesting behavior. I'm handling my errors in my class modules, and I bubble the errors up the stack. I have traceability so when the error reaches the caller (or top of stack) an error is displayed telling me where it occurred at. It works just fine if the class is being called from a module. BUT if the class is being called from the sheet or workbook, then it generates an unhandled runtime error.
For example, take the following code:
sub testError
err.raise -1000,,"This is a custom error"
end sub
If I run this in a regular module I get the following:
But if I run that exact same code in a sheet or workbook, I get the following:
So some of my classes are being called from macros located on a sheet. Others are being called from workbook and/or sheet events. And if a handled error happens down the line, I get an unhandled runtime error without any useful information.
I can move my macros to a standard module. But I can't move my events (value changes, new caluclation, etc) to a standard module.
So I have two questions. Why can't I do err.raise in a sheet / workbook?
What should I do instead?
Thanks.
So digging into this further. There are two kinds of error popups VBA will give you:
One with a continue/end/debug/help button
One with an okay/help button.
It turns out you can only modify the description with the popup that has the end/debug button. (via the err.raise).
What determines which popup you see is where the top of your stack is. If its in a standard module, you'll get the "end/debug" popup. If its in a sheet/workbook object, then you'll get the "okay/help" popup.
Unhandled errors bubble up all the way to the top stack. If that stack is in a standard module, then you'll have the option of pressing the debug button, which will take you to the line that you're error occurred. If the top stack is in a sheet/workbook, you'll have no idea where it happened.
To illustrate my point, take the following code that will throw a standard runtime error:
sub testError
dim a as long
a = clng("X")
end sub
If this method is ran in a standard module you'll get this:
If you run this in a sheet / workbook you'll get this:
The later doesn't really work well with err.raise (you can't edit the description).
My problem is I have macros and events that are initiated in the sheets / workbooks. Those methods then call other methods in a module. But since the top stack is located in a sheet / workbook, the wheels come off of my error handling.
So my work around is to initiate my module level methods in a way that puts those methods at the top of the stack, and then it works:
'standard module
sub createClass
dim myObj as myClass
set myObj = new myClass
call myObj.raiseError
end sub
Instead of calling this method from the sheet / workbook as so:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
call createClass
End Sub
Do this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
application.run "createClass"
End Sub
Doing it this way will place the createClass method at the top of the stack. and since this is in a standard module, the error handling will work as expected.
I'm trying to keep alive an old Excel VBA program, and my client has asked me to run some existing code on startup (instead of having to click a button).
So, I'm following this tutorial here.
It kinda works ... ie when the excel files is open the MsgBox prompts appear but with blanks or 0s. However that only happens when I don't include the first line:
Declare Function GetCommandLineA Lib "Kernel32" () As String
when I include that, I get this error message:
Compile Error: Constants, fixed-length strings, arrays, user-defined types and Declare statements not allowed as Public members of object modules.
So - how do I do it?
The Sub Workbook_Activate is Private and I don't think I can change that, can I?
Posting for other confused people:
I was trying to run it from within the ThisWorkbook sub, and what Pᴇʜ rightly pointed out was that you have to create this code in a normal module, and then call that module's sub FROM the ThisWorkbook sub.
thank you Pᴇʜ
Any help with dynamically loading of classes /variables in VBA?
I have an excel sheet and other few dependent sheets, depending on the requirement, on click of a button I am loading other sheets.
Say Main workbook -> Loads sheet1.xlam and related classes ( cls_one.cls) on click of button.
I am using below code to load sheet1.xlam and cls_one.cls
Step 1: Load Main.xlsm
Step 2 load - sheet1.xlsm
Set addedWs = ActiveWorkbook.Sheets.Add(Type:=path, after:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.count))
Step 3: Load Cls_one.cls dynamically using the below method.
Public Function InsertClass(ByRef oWB As Workbook, ByVal className As String, ByVal filePath As String, Optional bUAT As Boolean = False) As Boolean
On Error GoTo Errhandler
oWB.VBProject.VBComponents.Import filePath
InsertClass = True
Exit Function
Errhandler:
If err <> 0 Then
If Not bUAT Then
MsgBox (err.Description)
End If
err.Clear
End If
InsertClass = False
End Function
Above code works fine, however, I have a reference to cls_one in sheet1.xlsm which never works, on loading or give error Undefined object error.
Public Myclassone As cls_one
this variable declaration is in sheet1.xlsm.
I tried flipping steps loading class first and sheet1.xlam next, but still getting the same error with Excel 2013, this piece of code works fine with 2010.
Trying to understand what is the best way to reference dynamically loaded classes in forms or other classes?
Also, I tried changing error preferences in tools -> Options -> Break on Unhandledexception.
You can't early-bind to something that's only going to exist at run-time, by definition - there's no way that could have worked:
Public Myclassone As cls_one
If cls_one doesn't exist at compile-time, then the module can't be compiled.
this piece of code works fine with 2010
No. This piece of code works fine if it's not in any execution path that involves the module declaring a public variable of a type that doesn't exist... regardless of what version the host application is (this behavior is purely VBA, nothing to do with Excel).
That's kind of a hack though: a VBA project will not compile (through Debug -> Compile), but will happily run anyway if the entry point doesn't involve loading the module: that's because of the somewhat-interpreted nature of VBA.
Proof:
Module1
Option Explicit
Public foo As Something '<~ undefined, won't compile
Module2
Option Explicit
Public Sub Test()
Debug.Print "I can run even if the project doesn't compile!"
End Sub
You can run Module2.Test regardless of whether the project compiles, because Module1 isn't in the picture at all. Now change Module2.Test to this:
Public Sub Test()
foo.DoStuff '<~ expect fireworks
End Sub
When I ran this, Excel just outright crashed.
So the bottom line is this: you can reference a non-existing class in a module. The project won't be compilable, but if no code references the non-existing class then the project will be executable anyway, and the non-compilable module can then be executed in another execution context (i.e. from a separate entry point), after the class is added.
I would not recommend using non-compilable code for anything remotely important though, since doing that takes away the only compile-time validation you have in the VBE for your code - consider reviewing Rubberduck inspections in that case (several inspections flag run-time error situations statically).
A better, more viable solution would be to reconsider the dependency chain and the overall approach.
I was trying to put MsgBox into my code. It should be shown only if call Table.auto_open doesn't work.
In my final document are few of those Call statements and i would like to get only that msgbox, if one or few of Call statements doesn't work.
For Example "Auto_open" will be changed to "auto_op" what naturally won't be possible, because in real sheet it is "Auto_open".
Or in another example that code from "Auto_open" is broken.
I need some help with that. It's seems to be simple, but I think it's not possible to put that "On Error GoTo" code in that place just like that, because Call doesn't give me a real error?
Can someone say me what I'm doing wrong? I tried already all combinations of that Error handling, nothing works.
Private Sub Workbook_Open()
On Error GoTo Error
Call Tabelle1.auto_open
Exit Sub
Error:
MsgBox "Failure"
Resume Next
End Sub
If you are calling a sub that does not exist you will get a Compile Error.
You can check for these errors by going to VBE>Debug>Compile VBAProject (or just try to run the macro)
Compile Errors, much like Syntax Errors, have to be handled before you can run a sub. Thus, these errors cannot be handled with code such as On Error GoTo EH or On Error Resume Next as these are only activated once the sub is actually running.
You can convince yourself of this by producing a common Compile error, or Syntax error, and trying to step through the code (F8). You will notice that the error occurs on your Sub [Name] () line, which indicates that you never actually entered your sub before the error occurred. Thus, you can intuitively see that your error handler will never actually be activated, resulting in an error message being displayed.
Once you have accounted for all Compile/Sytax Errors, you can check out this link, which will explain how you can handle Run Time Errors when you are calling other sub procedures from a sub.
I have reviewed other questions related to calling a module within a sub but the solutions do not seem to be applicable to my case. I am trying to run code within a module when there is a change within a userform textbox which I have named filepath1.
Below is a copy of the code I am trying to run. Each time I get a compile error "Expected variable or procedure, not module." To confirm the name of my module is not the same as any other name in the sub or userform. Any advice is appreciated!
Private Sub Filepath1_Change()
Call ChangeFilepath
End Sub
For the most part, the module can be safely ignored. You're not trying to run a module, you're trying to run a subroutine that's stored in a module, and as long as it's not set to private you shouldn't need to specify the module name.
If you had a macro called "ChangePath" in a module called "UpdateFilepath", your call would be Call ChangePath.