VBA Test for Object Model - vba

I'm trying to write a PPT macro that will work in both PPT 2010 and 2013-2016. There is one object in the 2013-16 Object Model (ActivateChartDataWindow) that is not in the 2010 model that I want to use. I thought of using code like this to test the application version and then use the correct object:
With theChart.ChartData
If CInt(Application.Version) >= 15 Then
.ActivateChartDataWindow
Else
.Activate
End If
....
End With
The problem is this won't compile in 2010 since the .ActivateChartDataWindow object is not found. So there wouldn't be run-time error, but there is a compile-time error.
What's the best way to do this? Is there a way to disable compile-time checking in the code itself?

You're making an early-bound member call; if the code contains member calls that can't be compiled using an earlier version of a type library, then the solution is to switch to late-bound calls, which by definition are only bound at run-time (i.e. no compile-time validation).
So instead of With theChart.ChartData, you declare an Object variable, and Set it to theChart.ChartData:
Dim lateBoundChartData As Object
Set lateBoundChartData = theChart.ChartData
And now any member calls made against that lateBoundChartData will only be validated at run-time - watch out for typos, Option Explicit can't help you here!
With lateBoundChartData
If CInt(Application.Version) >= 15 Then
'no intellisense & compile-time validation here
.ActivateChartDataWindow
Else
'no intellisense & compile-time validation here
.Activate
End If
End With
The funny thing is that people write late-bound code all the time, without even realizing: anything you write against Object is always going to be late-bound.

Related

vba code suddenly encounters problems with enumeration

This visual basic code related to outlook used to work without problems
Sub cmdExample()
Dim myOlApp As Object
Set myOlApp = CreateObject("Outlook.Application")
Set myoSession = myOlApp.Session
Set myoCalendar = myoSession.GetDefaultFolder(olFolderCalendar)
End Sub
Now I obtain the runtime error 5 (Invalid procedure call or argument)
Reason found from debugging: at runtime olFolderCalendar is empty (by the way, same problem for other enumerations like olAppointmentItem, olBusy). My workaround in the above code is calling .GetDefaultFolder(9).
However I would rather use the enumerations and I would like to understand why all of them are empty.
If you want to pass literal constants instead of numbers you need to add a COM reference to Outlook. Read more about that in the How to Add an Object Library Reference in VBA article.
Also, you may try to use the Logon method before getting the folder. See NameSpace.Logon for more information.

Access an application and instantiate an object

I am trying to create an instance of a specific class called ExtraScreen from a referenced library application called EXTRA. How can I use the SendKeys function from the ExtraScreen class?
So far I tried this:
Dim software As EXTRA.ExtraScreen
software.SendKeys ("a")
The result is Error:
Object variable or With block variable not set.
You have to also Set it to something:
Dim software As EXTRA.ExtraScreen
Set software = CreateObject("EXTRA.ExtraScreen")
or
Dim software As New EXTRA.ExtraScreen
You have declared an object variable with a specific, early-bound type - which means if the code can compile & run, then the project has a reference to the type library.
Dim software As EXTRA.ExtraScreen
Dim statements aren't executable: you can't put a breakpoint on a Dim statement. All they do is allocate a spot of a given size in memory. In this case, it reserves a spot wide enough to hold an object reference - and nothing else.
On execution, the first statement to execute is this:
software.SendKeys ("a")
But the problem is, if you put a breakpoint here and inspect the Locals toolwindow, you'll find that the software object contains Nothing. In other languages this is known as a "null reference" - the object variable is not set: there's a reserved spot for holding an object reference, but the spot is empty.
You use the New keyword to create an instance of a class - i.e. to create an object. And in VBA object reference assignments require the Set keyword:
Set software = New EXTRA.ExtraScreen
Now if you run that line and inspect your locals, you'll find that software isn't Nothing anymore, and you can inspect its state / properties.
Once an object variable holds a proper object reference, you can legally invoke its members:
software.SendKeys "a"
You can never invoke anything on a Nothing object reference: an object variable that is Nothing is, well, nothing: it's not an object, therefore it has no members to invoke. VBA runtime responds by throwing run-time error 91 "Object (or With block variable) not Set".
The "With block variable" part is referring to the With keyword, which can also hold objects. For example you could do this:
With software
.SendKeys "a"
End With
And you'd get the exact same error if software isn't Set. Consider dropping the local variable altogether, if it only ever needs to live as a local variable inside some specific procedure:
With New EXTRA.ExtraScreen
.SendKeys "a"
End With
In this case the With block is holding the object reference; at End With, the object is gone (avoid jumping in & out of With blocks, specifically for that reason).

Error 424: object required when calling external library

I'm writing a VBA macro, and have imported mscorlib.dll in order to refer to System.Math.
Sub draw16mmButtonHole()
Dim test As Double
test = System.Math.ASin(0) 'this is where the error happens
End Sub
Apparently Error 424 usually happens when calls return objects, and can be fixed by appending set to the variable assignment; however, here the same error happens.
In addition to the behavior described above, Error 424 happens when trying to call .NET functions from VBA. This is not something VBA is able to do.

VBA User form with ThemeColorScheme and late binding

I'd like to run a user form with status bar.
I show my form with code bolow.
How should I declare variables and assigning new values to those variables?
Very important: I have to use late binding in my project.
Sub RunMyUserForm()
With MyUserForm
.LabelProgress.Width = 0
.TextBox1 = 1
'to make the progress bar color match the workbook's current theme:
.LabelProgress.BackColor = ActiveWorkbook.Theme.ThemeColorScheme.Colors(msoThemeAccent1)
.Show vbModeless
End With
End Sub
Thank you in advance for your help!
Updated information:
When I try to run my macro with "Option Explicit", it doesn't work (Compile error: Variable not defined - part of code msoThemeAccent1 is marked as yellow color). That's why I asked for help in defining the variables.
When I try to run my macro without "Option Explicit", it dosen't work (Err.Description: "Value is out of range", Err.Number: -2147024809)
When I try to run my macro with early binding (reference to "MS Office Object Library" via Tools/References in VBE) everything works perfect with (and without) "Option Explicit".
Your compiler is seeing msoThemeAccent1 as a variable, and it is undeclared. This is why your code won't run with Option Explicit and also why your code raises an error when you disable Option Explicit. .Colors is a 1-based collection (?) so when you call:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(msoThemeAccent1)
It is compiling to:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(0)
Which raises an expected, albeit cryptic error.
I used some code to check the value of this constant, but in hindsight I should have just referred to the documentation:
http://office.microsoft.com/en-us/excel-help/HV080559557.aspx
This should fix it for you
ActiveWorkbook.Theme.ThemeColorScheme.Colors(5)
Alternatively, if you need to rely on this value in several places in your code, you could declare a variable (public or private, scope depends on what you need it for).
Public Const myAccentColor1 as Long = 5
And then, in your code you could:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(myAccentColor1)
Revised
I understand now, without reference to MS Office Object Library this makes sense. This is one of the few libraries that I maintain a reference to in my XLSB file, so my confusion was a result of the fact that I thought I was using an appropriate late-binding, but I was really using early-binding.

VBA: Run time error '91'?

All I'm trying to do here is save a reference to the currently active window, but it doesn't seem to be working. It gives me a run time error on the last line.
Dim SourceWindow As Window, QACheckWindow As Window
SourceWindow = ActiveWindow
I'm not exactly sure why. Isn't ActiveWindow supposed to return the currently active window? If not, how can I make a reference to it?
EDIT: The above is right at the beginning of my function, so all there is before it is Sub FuncName()
In VB object variables require the Set keyword to be assigned. Object properties that are objects also need to be Set. Runtime error 91 "object variable not set" is raised when the assignment doesn't use that keyword.
This is inherited from legacy Let keyword to assign values, and Set keyword to assign references; the Let eventually was deprecated (although still needed for defining properties) and the Set remained, leaving the VB6/VBA value assignment syntax like [Let] variable = value, where "Let" is optional.
In the declaration and assignment:
Dim SourceWindow As Window, QACheckWindow As Window
'this is like saying "Let SourceWindow = ActiveWindow":
SourceWindow = ActiveWindow
SourceWindow is an object, assigned as if it were a value - this causes VBA to attempt let-coercion through a default member call. If the object wasn't initialized, the member call fails with error 91. If the object was initialized but doesn't have a default member, error 438 is raised.
So in this case error 91 is being raised because of an implicit member call; the .net equivalent would be a NullReferenceException:
Dim SourceWindow As Window, Dim WindowTitle As String
'"SourceWindow" reference isn't set, the object can't be accessed yet:
WindowTitle = SourceWindow.Caption
I'm going to go a bit overboard here, but the legacy Let statement should not be confused with the Let clause (in VB.net) which, in the LINQ query syntax (in VB.net), computes a value and assigns it to a new, query-scoped variable (example taken from MSDN):
From p In products
Let Discount = p.UnitPrice*0.1 '"Discount" is only available within the query!
Where Discount >= 50
Select p.ProductName, p.UnitPrice, Discount
VB.net assigns both values and references, without the need to specify a Let or a Set, because in .net this distinction is a much thinner line, given how everything ultimately derives from System.Object... including System.ValueType. That's why the Set keyword was also deprecated in VB.net, and also why the VB.net syntax for defining properties has dropped the Let in favor of Set - because parameterless default members are illegal in VB.NET, so this ambiguous let-coercion doesn't happen.