COM Exception (code 0x800A03EC) thrown when programmatically changing page breaks in VB.Net - 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.

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.

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.

32 VB appl automating with 32 bit MS Office

When running a 32 bit VB application on Win 7 64 bit with 32 bit office, everything works fine. When running with 64 bit Office (specifically Word), most of the code works fine, but the .Paste method of the Word.selection class fails with 'This command is not available'. The user can use ctrl-V at that point to paste the contents. How can the VB code be changed to support both 32 and 64 bit Word?
oWorkDoc = oWord.Documents.Open(filename, , True, False)
oWord.Selection.Copy()
cell.Select() <--- refers to cell in table in another document
Try
oWord.Selection.Paste() <--- failing statement
Catch ex As Exception
oWord.Visible = True
MsgBox(ex.Message)
End Try
Update: the user is NOT using Office 64 bit. He is using 32 bit. So the mystery thickens. Also, the code sequence is used in other portions of the application, and work fine. Also the Try/Catch block has been changed to just ignore the exception and the paste action is occurring.
... selections that include table cells can also lead to
unpredictable behavior. The Information property will tell you if a
selection is inside a table.
Because Range objects share many of the same methods and properties as
Selection objects, using Range objects is preferable for manipulating
a document when there isn't a reason to physically change the current
selection.
Source: Selection interface - Remarks
I advise avoiding use of the Selection object as much as possible. Instead use a typed reference of the appropriate type; in this case a Word.Range. Copy and Paste are methods on the Word.Range object and should be available on a valid reference. Assuming that cell is a Word.Cell object reference, use its Range property.
So instead of:
cell.Select() <--- refers to cell in table in another document
oWord.Selection.Paste() <--- failing statement
use:
cell.Range.Paste

convert early binding to late binding without changing object type

This seems like a simple question but I after chasing forums for several hours I think it might be impossible.
I often want to convert a program from early binding to late binding. Usually, it is a vba, visual basic for applications, program that runs under Excel 2010 and windows 7 pro.
For discussion purposes, let’s pretend it is the following.
Sub EarlyBind()
' use IDE > Tools > references > and select “Microsoft Internet Controls”
Dim shellWins1 as shdocvw.shellwindows
Line1: Set shellWins1 = New SHDocVw.ShellWindows
MsgBox TypeName(shellWins1) ' this will display “IShellWindows”
' other code that expects to be working with an IshellWindows object …..
End Sub
In my experience, converting such a program to late binding is sometimes hard.
For instance, I found some forums that suggest I change it to
Set shellwins1 = createobject("Shell.applicaton")
But that creates a IShellDispatch5 object, not an IshellWindows object. That means I have to change other code to accommodate the new object type. And, of course I have to test that other code for subtle differences.
So, my goal is to find a general solution that will allow me to rewrite “Line1” to create the CORRECT object type with late binding. I also wish to avoid the need setting a reference to "Microsof Internet Controls. In other words, I want the code to look like this:
Sub LateBind()
Dim shellWins1 as object
Line1: Set shellWins1 = createobject(“xxxxxx.yyyyyy”).zzzzzz
MsgBox TypeName(shellWins1) ‘ this should display “IShellWindows”
….. other code that expects to be working with an IshellWindows object …..
End Sub
I know how to use the vba IDE to find the dll associated with the object. In this case the dll is Library SHDocVw C:\Windows\SysWOW64\ieframe.dll.
I have installed OleView and can find the associated IshellWindows “magic numbers” for the clsId, TypeLib, and Inteface (for instance the interface is 85CB6900-4D95-11CF-960C-0080C7F4EE85).
But, I don’t know how to convert them into a program id that can be used in line1 in the sample code posted above.
I hope someone here can help.
------ With MeHow's help, I now have the answer! ------
To switch 'set myObj = new xxxx.yyyyy' to late binding for arbitrary object types
Change set myObj = new xxxx.yyyyy
into set myObj = CreateObject("xxxx.yyyyy")
Very often that will work.
But, in the some cases, (e.g. "shDocVw.ShellWindows.") it gives error 429 ActiveX component cannot be created.
When that occurs I AM COMPLETELY OUT OF LUCK. It is impossible to use late binding with that EXACT object class. Instead I must find a substitute class that does approximately the same thing. (e.g. "Shell.Application").
Your short answer is
IShellWindows is an interface.
It
Provides access to the collection of open Shell windows.
Therefore
Take a look at the CreateObject() method.
Note:
Creates and returns a reference to a COM object. CreateObject cannot
be used to create instances of classes in Visual Basic unless those
classes are explicitly exposed as COM components.
IShellWindows is not exposed as a COM component so that's why there is no way to say CreateObject("SHDocVw.IShellWindows")
When you open your registry (regedit) and search for a key type in IShellWindows. If you find anything that means you've found your Prog ID and if you don't find anything it means that nothing like IShellWindows is registered as a prog Id therefore it would make sense to assume that you can't late bind IShellWindows
I bumped into your question trying to find something for myself. But I don't know if you have tried the following -
Set shellwins1 = createobject("Shell.Application")
MsgBox TypeName(shellWins1.Windows)
This answers your question for datatype. It prints IShellWindows for me. I'm not sure though if it could actually solve your purpose for latebinding meaning if this would be the object required though the datatype is what you need.
So, I would advise you to give it a try.
There is a slightly better approach outlined at https://www.experts-exchange.com/questions/28961564/How-to-find-the-class-id-of-an-arbitrary-object-Example-Set-x-CreateObject-New-1C3B4210-F441-11CE-B9EA-00AA006B1A69.html#a41743468.

Need to call com function from VB.NET

If anyone wants to take a crack at this I'd really appreciate it. I'm writing a VB.NET app that will control a commercial backup product. One of the things I need to do is loop through all existing jobs and look at the source drive. I am able to do this in VBScript very simply like this:
Dim SP, BackupJob, volumes
Set SP = CreateObject("ShadowStor.ShadowProtect")
For Each Job In SP.Jobs
Set BackupJob = SP.Jobs.GetBackupJob(Job.Description)
BackupJob.GetVolumes volumes
For Each Volume in volumes
WScript.Echo volume
Next
Next
Set SP = Nothing
However nothing I try in VB.NET works. I'm pretty sure it has to do with the fact that the com functions are returning variant data types and arrays (specifically GetVolumes). I have tried using string arrays, object arrays, and even wrapping the return value in a VariantWrapper and I always get errors such as "not implemented" or "the parameter is incorrect." If anyone is bored and wants to write some code I'll gladly give it a shot and report back.
UPDATE:
This is odd. Look at this code:
Dim SP As Object = CreateObject("ShadowStor.ShadowProtect")
Dim gotJob As Object
Dim volumes() As Object
Try
For Each Job As Object In SP.Jobs
gotJob = SP.Jobs.GetBackupJob(Job.Description.ToString())
gotJob.GetVolumes(volumes)
For Each volume As Object In volumes
MsgBox(volume.ToString())
Next
Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
This will display the volume from ONE job, then it crashes if there is more than one jobwith the error "invalid callee."
Locate ShadowStor.ShadowProtect in your registry in HKCR. It will have a CLSID which is a GUID. Search for that GUID, also in HKCR. You should find it in the CLSID section. Under that key you should find the actual dll path under InprocServer32.
Now if that component has an embedded TypeLib you should be able to add a reference to it in Visual Studio. (If you have OLE View installed you can inspect the type lib easily as well).
And if you cannot add a reference to the dll, there might be a seperate .tlb file, and you can find that by searching on the GUID present in the TypeLib value.
For anyone interested, the solution was to Dim volumes() As Object inside the loop and then set volumes = Nothing at the end of the loop so that it was re-created each time. If anyone can explain why this is so I would love to understand it.