vba code suddenly encounters problems with enumeration - vba

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.

Related

Late Binding & Type Issues In VB

I'm trying to run my code which was originally created using Visual Studio through another application where late bindings are disallowed and this option cannot be altered unfortunately. I am very new to programming in general and struggling to get my head around the issue. Here is the code im using in the invoke code stage:
Dim objIEShell As Object = CreateObject("Shell.Application")
Dim objIEShellWindows As Object = objIEShell.Windows
Dim objIEWin As Object
For Each objIEWin In objIEShellWindows
If InStr(objIEWin.LocationURL,"google")>0 Then
objIEWin.Quit
objIEWin = Nothing
End If
Next
The code simply closes all instances of Internet Explorer with "google" in the URL. This is the error message I get when trying to compile it:
Message: Error compiling code
error BC30574: Option Strict On disallows late binding. At line 2
error BC32023: Expression is of type 'Object', which is not a collection type. At line 4
From the research I've done so far I realise the first error message on line 2 is to do with the type difference between objIEShell and the Windows method. I think I have to convert objIEShell like this, CType(objIEShell,?), but I don't know the type of the .Windows method or how to find this out. Also any insight on how the fix the second error would be greatly appreciated as I'm not sure where to start with that one either.
This dates back to the wonky days when Microsoft still had plans to make Explorer behave like a web browser. Makes it pretty hard to arrive at the correct code, it is a combination of two separate COM components that don't have much to do with each other.
You need to first add two references to those components so the compiler understands the names. Use Project > Add Reference > COM tab and tick "Microsoft Internet Controls" and "Microsoft Shell Controls and Automation". That adds the Shell32 and SHDocVw namespaces.
Now you can write the code early-bound like this:
Dim objIEShell = New Shell32.Shell
Dim objIEShellWindows = CType(objIEShell.Windows, SHDocVw.IShellWindows)
Dim objIEWin As SHDocVw.WebBrowser
For Each objIEWin In objIEShellWindows
If InStr(objIEWin.LocationURL, "google") > 0 Then
objIEWin.Quit()
End If
Next
The CType() expression is probably the most unintuitive one, the Shell.Windows property is of type Object to break the dependency between those two components. The cast is the necessary voodoo to keep the compiler happy.

VBA Test for Object Model

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.

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: Access to registry while preprocessing

I want to conditionally compile code in one VBAproject, with a condition that depends on some registry-entry. Is this somehow possible in VBA?
I know that there are some simple preprocessing possibilites in VBA, but I can not see if it is possible to somehow access the registry while preprocessing. Or Maybe some other possibility to check the registry before compiling.
Since I get a compile error because of some missing reference(and thus missing class object), I aim to check the registry before compiling.
P.s. I only want to read registry-entries.
As an example. How to reach debug.print in the following, i.e. avoiding compile errors.
sub sub1()
dim testobj as new nonexistingobject
sub2 testobj
debug.print "Arrived at this point"
end sub
sub sub2( byref testobj as nonexistingobject)
*do some stuff with testobj*
end sub
Instead of using early binding (Dim obj as myObject), use late bindings with CreateObject. This way you will be able to handle the case where the object doesn't exists:
Sub test()
Dim obj As Object
On Error Resume Next
obj = CreateObject("myObject")
if Err then Exit Sub 'if the object wasn't found exit the function
on error goto 0 'set back the error handling to its previous state
'rest of the code
End Sub
VBA does not directly provide access to the entire Windows Registry, but you can use a workaround.
Dim RegObj as Object
Set RegObj = CreateObject("WScript.Shell")
RegObj.RegDelete RegKeyString
RegObj.RegWrite RegKeyString
Str = RegObj.RegRead RegKeyString
Set RegObj = Nothing
If the RegKeyString is not found, it'll throw an error, so you need some OnError -> Key Doesn't Exist kind of code.
Not sure of the preprocessing part, but you could run your code in the Workbook_Open event handler to make sure this part runs before anything else.
It is not feasible. All the symbols and conditions that are tested by VBA Directives are build from literals or expressions that include only operators (excepting the Is operator, I think).
Function calls are not allowed when declaring directive symbols and conditions, and the only way to access registry in VBA is via API (like WScript.Shell for example), which means function call.
Further reading: http://msdn.microsoft.com/en-us/library/tx6yas69.aspx

COM Exception (code 0x800A03EC) thrown when programmatically changing page breaks in VB.Net

I am attempting to use the VB.Net Excel COM interop to programmatically change the location of the first horizontal page break on an Excel spreadsheet being generated by the program. Code to do so is as follows:
Dim range As Excel.Range
xlActualWS.Activate()
xlActualWS.PageSetup.PrintArea = "$A$1:$K$68"
range = xlActualWS.Range("A68", "A68")
xlActualWS.HPageBreaks(1).Location = range
System.Runtime.InteropServices.Marshal.ReleaseComObject(range)
On the line setting HPageBreaks, COM exception code 0x800A03EC is thrown, and I can't really find a thing related to this searching. Anyone have any idea what I'm missing here?
Based onthe code it looks like either the location of the page break cannot be set or that there are 0 page breaks and hence you're accessing an invalid index. A quick way to test this out is to do the following
Check the Count property on xlActualWS.HPageBreaks and see how many are available
Remove the set of the Location property and see if the error dissapears
Additionally you should probably remove the ReleaseComObject call. That's a very difficult API to get correct and the CLR is quite good at cleaning up COM object references on it's own.