Excel VBA: Runtime Error 424, Object Required - vba

I can't figure this issue out. I had a similar problem before, it turned out I was using a Range of a single cell, which was redundant. But in this case I need End(xlDown) and I can't get it to work. I tried a few combinations and I can't figure out the right syntax. Help?
Public Exceptions As Range
Public Xcept As Range
Sub Example()
Static ExcSh As Worksheet
Set ExcSh = Worksheets("ComboExceptions")
Set Exceptions = ExcSh.Range("A2")
Set Xcept = ExcSh.Range(Exceptions.Offset(1).Cells, Exceptions.Offset(1).Cells.End(xlDown))
'This is where the error happens ^
End Sub

Solved it! I had declared
Static ExcSh As Worksheet
in another Sub making it inaccessible to the function that errored. I made it public and now the following command works fine:
Set Xcept = ExcSh.Range(Exceptions.Offset(1), Exceptions.Offset(1).End(xlDown))

Related

Generic Way to Determine if Invoking a Property Throws an Error

Say you have one slide with one chart on it, and you run this code(in a version of Office later than 2007):
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
Set result = pptWorkbook.ContentTypeProperties
You will generate an error:
Application-defined or object-defined error
I believe this is because "Smart tags are deprecated in Office 2010."(Source), Generally to avoiding this sort of issue from throwing an error and exiting your VBA you can take one of two different approaches:
//Method 1
If OfficeVersion <= 2007
Set result = pptWorkbook.ContentTypeProperties
//Method 2
On Error Resume Next // or GOTO error handler
Set result = pptWorkbook.ContentTypeProperties
Method one requires that you know the specific reason why the property would cause an error, which is easy in this case but may not be as easy with other properties. Method two requires that you use some form of error handling to deal with the error AFTER the fact, my understanding of most other Microsoft languages is that is typically discouraged(example, another example). Is this standard practice in VBA?
In VBA, is there any other way to determine whether a property of an object would throw an error if invoked, BEFORE invoking that property, and without knowing the specifics of that invoked property?
What I like to do for this situation is create a separate function that checks if the property exists and returns a Boolean. In this case it would look something like this:
Public Function CheckIfExists(targetObj As Object) As Boolean
Dim testObj As Object
On Error GoTo failedTest:
Set testObj = targetObj.ContentTypeProperties
CheckIfExists = True
Exit Function
failedTest:
CheckIfExists = False
End Function
Which would return false if that property causes an error and true if not-
Then modify your sub to be:
Public Sub FooSub()
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
If CheckIfExists(pptWorkbook) Then
Set result = pptWorkbook.ContentTypeProperties
End If
... rest of your code or appropriate error handling...
Hope this helps,
TheSilkCode

VBA use global variable in userform code

I'm working in Excel 2010 VBA. Is there a way of accessing values in global variables declared outside a userform, in code inside the userform? Code inside my userform returns the global variable as null - can't work out why!
The variable is declared in the ThisWorkbook module as:
Public TargetCell As Range
Public TargetCellWorksheet as Worksheet
Public CurrentValue As Long
Inside the userform, I have this code on the "Update" button:
Private Sub Update_Click()
MsgBox ("Start of Update sub. TargetCellWorksheet =" & TargetCellWorksheet)
End Sub
The msgbox returns "" for the variable.
Hoping someone may be able to help me understand this and how to access the variable inside the userform? Thank you in advance
As for the problem itself, you declare
Public TargetCellWorksheet as Worksheet
and then try to show it into a MsgBox:
MsgBox ("Start of Update sub. TargetCellWorksheet =" & TargetCellWorksheet)
Did you maybe mean TargetCellWorksheet.Name, or TargetCellWorksheet.Range("A1").Value, since the MsgBox expects to receive a string?
However, if you're sure about your code, it might depend on the fact that the variable is not properly declared as Public and it goes at module level only. You might want to add a property to your form, if the variable is part of the form itself (I assume that you meant to use CurrentValue, but you can simply change the type of the property from Long to Worksheet and use it instead):
This goes inside the code of your form
Dim pCurrentValue As Long
Public Property Get CurrentValue() As Long
CurrentValue = pCurrentValue
End Property
Public Property Let CurrentValue (value As Long)
pCurrentValue = value
End Property
Hence, passing the variable from the module to the form like this:
This goes into your module, before you enter the code of the form
Dim myForm As New yourForm
myForm.CurrentValue = whateverYourVariableIs
and so using the variable inside your form like this:
You can hence use your variable by calling it from the property of the form
myVariableInTheForm = Me.CurrentValue
I must say that, however, it is strange that a public variable is not reaching the stack of the form. Are you sure you're not only declaring the variable without assigning any value before?

Excel VBA - QueryTable AfterRefresh function not being called after Refresh completes

I am developing an Excel (2010+) Application using VBA and have run into an issue where the AfterRefresh event function is not being invoked once the query finishes executing.
I have not been able to find many decent resources or documentation for how to have this event function triggered in a Class Module. I decided to use the Class Module design route instead of putting the event handlers in the worksheet after receiving a response to an earlier question about QueryTables (found here Excel VBA AfterRefresh).
Here is the code for my Class Module called CQtEvents
Option Explicit
Private WithEvents mQryTble As Excel.QueryTable
Private msOldSql As String
' Properties
Public Property Set QryTble(ByVal QryTable As QueryTable): Set mQryTble = QryTable:
End Property
Public Property Get QryTble() As QueryTable: Set QryTble = mQryTble:
End Property
Public Property Let OldSql(ByVal sOldSql As String): msOldSql = sOldSql:
End Property
Public Property Get OldSql() As String: OldSql = msOldSql:
End Property
Private Sub Class_Initialize()
MsgBox "CQtEvents init"
End Sub
' Resets the query sql to the original unmodified sql statement
' This method is invoked when the Refresh thread finishes executing
Private Sub mQryTble_AfterRefresh(ByVal Success As Boolean)
' Problem is here
' This function is never called :( Even if the query successfully runs
Me.QryTble.CommandText = Me.OldSql
End Sub
Here is a quick snapshot of the code the creates an instance of this class, finds a relevant QueryTable, then calls Refresh
Option Explicit
Sub RefreshDataQuery()
'Dependencies: Microsoft Scripting Runtime (Tools->References) for Dictionary (HashTable) object
'From MGLOBALS
cacheSheetName = "Cache"
Set cacheSheet = Worksheets(cacheSheetName)
Dim querySheet As Worksheet
Dim interface As Worksheet
Dim classQtEvents As CQtEvents
Set querySheet = Worksheets("QTable")
Set interface = Worksheets("Interface")
Set classQtEvents = New CQtEvents
Dim qt As QueryTable
Dim qtDict As New Scripting.Dictionary
Set qtDict = UtilFunctions.CollectAllQueryTablesToDict
Set qt = qtDict.Item("Query from fred2")
''' Building SQL Query String '''
Dim sqlQueryString As String
sqlQueryString = qt.CommandText
Set classQtEvents.QryTble = qt
classQtEvents.OldSql = sqlQueryString ' Cache the original query string
QueryBuilder.BuildSQLQueryStringFromInterface interface, sqlQueryString
' Test message
MsgBox sqlQueryString
qt.CommandText = sqlQueryString
If Not qt Is Nothing Then
qt.Refresh
Else
' ... Error handling code here...
End If
''' CLEAN UP '''
' Free the dictionary
Set qtDict = Nothing
End Sub
Also here is a screenshot of the Module structure http://imgur.com/8fUcfLV
My first thought on what might be the issue was passing the QueryTable by value. I am not the most experienced VBA developer, but I reasoned this would create a copy and be calling the event on an unrelated table. However, this was not the case and passing by Reference did not fix the problem either.
Also the query is confirmed to run successfully as the data is correctly showing up and being refreshed.
EDIT
I added the BeforeRefresh event function to CQtEvents class Module and confirmed this function is called once Refresh is called
Private Sub mQryTble_BeforeRefresh(Cancel As Boolean)
MsgBox "Start of BeforeRefresh"
End Sub
How might I alter this code get my QueryTable from the QTableModule's RefreshDataQuery() Sub routine to have the AfterRefresh function invoked when the query is successfully ran?
How to catch the AfterRefresh event of QueryTable?
Explanation: in your situation, before event was fired you lost reference of your QueryTable by setting it to nothing when you made cleaning or procedure ended.
General solution: you must be sure that your code is still running and/or you need to keep any references to your QueryTable.
1st solution. When calling QT.Refresh method set the parameter to false in this way:
qt.Refresh false
which will stop further code execution until your qt is refreshed. But I don't consider this solution to be the best one.
2nd solution. Make your classQtEvents variable public and after RefreshDataQuery sub is finished check the status with some other code.
in you CQtEvents class module add the following public variable:
Public Refreshed As Boolean
in your BeforeRefresh event add this:
Refreshed = False
in your AfterRefresh event add this line of code:
Refreshed = True
Make your classQtEvents variable declaration public. Put this before Sub RefreshDataQuery()
Public classQtEvents as CQtEvents
but remove appropriate declaration from within your sub.
Now, even your sub is finished you will be able to check status of refreshment by checking .Refreshed property. You could do it in Immediate or within other Sub. This should work for Immediate:
Debug.Print classQtEvents.Refreshed
3rd solution. (a bit similar to 1st one) Follow steps 1 to 3 from 2nd solution. After you call qt.Refresh method you could add this loop which will stop further code execution until qt is refreshed:
'your code
If Not qt Is Nothing Then
qt.Refresh
Else
' ... Error handling code here...
End If
'checking
Do Until classQtEvents.Refreshed
DoEvents
Loop
Final remark. I hope I didn't mixed up qt variable with classQtEvents variable. I didn't tried and tested any solution using your variables but wrote all above with referenced to code I use.
A github repo that demonstrates the minimum code needed to get this working can be found here.
As mentioned, if your event handler isn't in scope, or your QueryTable reference is lost, you won't catch the event. The key factors to ensuring you catch the event are:
Declare a global variable of your event-handling class module's type outside of any subroutines/methods, at the top of a file (I chose the ThisWorkbook file).
Add a Workbook_Open event handler and instantiate that variable there, so that it is available immediately and will remain in scope (since it's global).
At that point, or at any downstream point when you have a QueryTable you're interested in, pass that QueryTable to the global instance to wire up its events.
(It took me a couple tries to figure this out myself, when someone pointed me in this direction as an answer to this question.)

VBA runtime error 91 even though variable is set

I am struggling with a piece of code that places a custom class in a collection. Every time I run the code I get a runtime error 91. The error is occuring on the Set pjt = New CProject line.
Dim Projects As Collection
Private Sub BuildProjects()
Dim pjt As CProject
Set Projects = New Collection
Set pjt = New CProject '<-----ERROR OCCURS HERE
'Do some other stuff
End Sub
This is obviously the simplest form of the Sub, but it still throws the error! What am I doing wrong? Do I need to set the access for the CProject class file somehow?
All your help will be greatly appreciated.
The line Set pjt = New CProject is calling the constructor for CProject.
The error will most likely be in the constructor of CProject.
Error 91 is a null reference

VBA Excel "Variables are required"

Got a trouble in a VBA excel program.
Sub code(s)
...
code = t
End Sub
And then :
Sub CommandButton1_Click()
...
For i = 0 To size
current_y = code(string_array(i))
...
End Sub
When i run the program, i got this error "Variables are required" (not sure, i'm working on a japanese version of excel). Sub CommandButton1_Click is highlighted and code is selected inside CommandButton1_Click. Can't figure out why, though it must be simple...
You're trying to return a result from a Sub. Try declaring it as a function instead, as this is able to return values to the caller:
Function code(s)
...
code = t
End Function
If it makes it any clearer, on my English version the error message is:
Expected Function or variable
Does the code include Option Explicit? Perhaps the error translates to "Variable declaration required"? Try removing option explicit - if that fixes it remove that line, then go through checking all variables are declare (e.g. dim current_y as string).