CreateObject function for "Shell.Application" vs "InternetExplorer.Application" - vba

In an answer to this post VBA Macro For Already Open IE Window CreateObject() is used twice, once with "Shell.Application" and another time with "InternetExplorer.Application".
The first time it is used to reference an already open or already active window.
The second time it is used to create a new browser window.
Sub GetIE_LateBinding()
Dim IE As Object
With CreateObject("Shell.Application").Windows
If .Count > 0 Then
' Get IE
Set IE = .Item(0) ' or .Item(.Count - 1)
Else
' Create IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
End If
IE.Navigate "http://support.microsoft.com/kb/q176792/"
Set IE = Nothing
End With
End Sub
I tried the following code. It results in a new IE window each time even when there is already an instance open.
Public Sub Trial()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
End Sub
I can understand the behavior of "InternetExplorer.Application" - creating new IE application instance each time hence new window. But "Shell.Application" seems to fetch already open shell application instance and hence, we are able to browse the already open IE windows. Seems a bit confusing. What am I missing?
Note: I am guessing something related to class_initialize() for "Shell.Application".

IE and Explorer used to be the same program which is why Shell.Application lists open IE Windows. It is only doing this because they used to both be Explorer windows. You are lucky this meets your needs.
CreateObject is used to create a new object.
GetObject(filename) connects to an open file, and if not open, opens it.
Set xlBook = GetObject("C:\Users\David Candy\Documents\Super.xls")
msgbox xlbook.name
GetObject("","shell.application") connect to an existing object and fails if it's not running.
Set GetExcelApp = GetObject("", "Excel.Application")
Msgbox GetExcelApp
Don't get too hung up on objects. Many objects are just function libraries like Shell.Application. You created a new function library object.
From COM help.
**Component Automation**
Mapping Visual Basic to Automation
Visual Basic provides full support for Automation. The following table lists how Visual Basic statements translate into OLE APIs.
Visual Basic statement OLE APIs
CreateObject (ProgID)
CLSIDFromProgID
CoCreateInstance
QueryInterface to get IDispatch interface.
GetObject (filename, ProgID)
CLSIDFromProgID
CoCreateInstance
QueryInterface for IPersistFile interface.
Load on IPersistFile interface.
QueryInterface to get IDispatch interface.
GetObject (filename)
CreateBindCtx creates the bind context for the subsequent functions.
MkParseDisplayName returns a moniker handle for BindMoniker.
BindMoniker returns a pointer to the IDispatch interface.
Release on moniker handle.
Release on context.
GetObject (ProgID)
CLSIDFromProgID
GetActiveObject on class ID.
QueryInterface to get IDispatch interface.
Dim x As New interface
Find CLSID for interface.
CoCreateInstance
QueryInterface
© Microsoft Corporation. All rights reserved.

Related

Get Window Instance/Object From Handle (hWnd)

Working in VB/VBA, I have a window handle and I need to convert it to a window object/instance which I can use to access the window object's properties.
AccessibleObjectFromWindow has not proven useful.
How do I do this?
Update
Below is additional detail.
I am working with a window created using mshta.exe and configured with some scripts:
CreateObject("WScript.Shell").Run "%systemroot%\syswow64\mshta.exe about:""<head><script>moveTo(-32000,-32000);document.title='" & x86WindowSignature & "'</script><hta:application showintaskbar=no /><object id='shell' classid='clsid:8856F961-340A-11D0-A96B-00C04FD705A2'><param name=RegisterAsBrowser value=1></object><script>shell.putproperty('" & x86WindowSignature & "',document.parentWindow);</script></head>""", 0, False
For Each Window In CreateObject("Shell.Application").Windows
On Error Resume Next
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Error = Err.Number
On Error GoTo 0
If Error = 0 Then Exit For
Pause 0.01, True
Next Window
' Configure the window environment - global object variables are defined, one for each scripting object - they are instantiated by calling the Initialize routine
Getx86Window.execScript "var VBScript, JScript;"
Getx86Window.execScript "Sub Initialize() : Set VBScript = CreateObject(""MSScriptControl.ScriptControl"") : VBScript.Language = ""VBScript"" : Set JScript = CreateObject(""MSScriptControl.ScriptControl"") : JScript.Language = ""JScript"" End Sub", "VBScript"
' Initialize the window environment
Getx86Window.Initialize
x86WindowSignature is a function that returns a unique string or key.
I'm going through this process because Microsoft doesn't provide 64-bit versions of the VBScript and JScript engines and this process allows me to create 32-bit versions and use them from a 64-bit world.
To reuse this scripting container I need to look at the existing windows and query one of the properties I created:
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Normally I use this logic to find the window of interest:
' Look for an existing window
For Each Window In CreateObject("Shell.Application").Windows
On Error Resume Next
Set Getx86Window = Window.GetProperty(x86WindowSignature)
Error = Err.Number
On Error GoTo 0
If Error = 0 Then Exit Function
Next Window
But, if the application crashes or otherwise fails to close the scripting window, it remains open but is not listed with CreateObject("Shell.Application").Windows.
But I can find it using FindWindow:
WindowHandle = API_FindWindow("HTML Application Host Window Class", x86WindowSignature)
I'm stuck trying to convert the window handle to a VB/VBA "Window" object variable.
I tried a simple copy memory from the handle variable to the object variable but that failed.
It turns out I made an incorrect observation in my initial work. I thought that I was not getting all the windows returned to me that I had created. That is not true. This line:
CreateObject("Shell.Application").Windows
always returns all the windows that were created by Shell.Application. So I had at my disposal all the windows my application had created in the past.
When I posed my question, I thought that I could only get those "lost" window objects by using lower level Windows SDK calls and then somehow convert those hWnds into Shell.Application window object references. I never did figure out how to do that or if it is even possible but that need is no longer required.

Open Word Document Run-time error '424': Object required

I have a set of functions in an Access database where I am generating Word documents based on a fixed template in a folder.
I am doing this with the function shown below. For easier maintenance, I would like to be able to define the Word-template paths as public constants in the begining of my module. And therefore, I have been trying to create the adjusted function below.
Original function:
Function MyFunc(rs as DAO.Recordset)
Dim objWord As Object
Dim objDoc As Object
...
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open("C:\test_template.docx")
...
End Function
Adjusted function:
Public Const ReminderOneTemplate As Variant = "C:\test_template.docx"
...
Function MyFunc(rs as DAO.Recordset)
Dim objWord As Object
Dim objDoc As Object
...
Set objWord = CreateObject("Word.Application")
for the Word.Documents.Open method in the next, subsequent line of code I have tried this:
Set objDoc = objWord.Documents.Open(ReminderOneTemplate)
...
End Function
and
Set objDoc = objWord.Documents.Open(Chr(34) & ReminderOneTemplate & Chr(34))
...
End Function
But the function keeps returning Run-time error '424' Object required when I use a constant as input to the Word.Documents.Open method.
Can anyone explain why this is the case and what I am doing wrong. Is it not possible to pass a Constant to the Word.Open method?
Thanks.
I refactored your function, using the following sub procedure to test the basic code of creating a Word instance, and then opening an existing document using a constant for the document name.
I used Office 2007 to test the code, and everything worked fine. I don't see anything wrong with your code, what version of Access and Word are you using? Notice that I added some code to test whether the objWord variable is actually assigned a value by the call to CreateObject. I would suggest 2 things to help try to resolve the problem you are having:
1. use a String for the filename. I know the documentation for the Open method of the Documents collection says that the filename argument is a Variant, but the code does seem to work better if it is a String.
2. Make sure you set the instance of Word to be visible, otherwise you clutter up your system with invisible instances of Word (which will not be listed in Task Manager) and the only way to get rid of them is to restart the computer. If the Word instance is visible, you can switch to it and see if Word is displaying any error messages.
You may notice that the Word document my code opens is a macro-enabled .docm file. I did this because I tested whether a document with an Open event-handler that caused a runtime error would show the error message in the code in Access, but it does not.
When I first ran the code with the constant declared as a Variant, I did get an error, but not the Object required error that is giving you a problem. I then noticed that when I re-opened the Word document in Word, that I got an error message from Word that "the last time this document was opened it caused a serious error, are you sure you want to continue to open the document?" I would suggest you make sure that you can open your Word document in Word without errors or problems. I also suggest you add code similar to what I have in the example below to ensure that the objWord variable is indeed being initialized by the CreateObject method -- if CreatObject is failing to create an instance of Word, then objWord will still be Nothing, and might then produce an object required runtime error. (problems in the Registry can make CreateObject fail.)
I'm really sorry, but I have no idea why you are getting the error you are getting. I think if you redeclare the constant as a String, and ensure that you make the Word instance visible, that your code will work! The only problem I had when testing was that the document failed to open until I added the code to make the Word instance visible. But I did not get the same error that you are trying to overcome.
I have develop a library of Access VBA code for exactly this type of task -- using code in Access to create instances of Word, Excel, and to open documents and worksheets. If you think looking at a code library designed to provide easily called procedures and functions for inter-operability among MS Office applications, you can download it from here: [http://www.didjiman.com/business/vbademo/libMSOffice.htm] The code in the library has been tested to work in all versions of MS Office from 2003 though 2016, and is released to the public under the Gnu public license. The code is in a zip archive that contains an Access .accdb file with all the code, and a PDF document discussing the functions and procedures, and how to use them, along with complete code listings.
Public Const ReminderOneTemplate As String = "C:\users\matthew\documents\temp\test document1.docm"
Sub testWord_DocOpen()
Dim objWord As Object
Dim objDoc As Object
Set objWord = CreateObject("Word.Application")
objWord.Visible = True 'make the Word application window visible
objWord.Application.WindowState = 1 'maximize the Word application window
If (objWord Is Nothing) Then
Debug.Print "Word object NOT initialized."
End If
Set objDoc = objWord.Documents.Open(ReminderOneTemplate)
End Sub

How to run ms access macro from vb.net?

I'm trying to run a macro from ms access 2010 from vb.net 2010 ..
The code I used is
Dim oAccess As Access.ApplicationClass
'Start Access and open the database.
oAccess = CreateObject("Access.Application")
oAccess.Visible = True
oAccess.OpenCurrentDatabase("C:\Users\Yuganshu\Desktop\New folder (4)\WindowsApplication1\db.mdb", False)
'Run the macros.
oAccess.Run("Macro1")
'oAccess.Run("DoKbTestWithParameter", "Hello from VB .NET Client")
'Clean-up: Quit Access without saving changes to the database.
oAccess.DoCmd().Quit(Access.AcQuitOption.acQuitSaveNone)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oAccess)
oAccess = Nothing
This is giving me following error:
Unable to cast COM object of type 'Microsoft.Office.Interop.Access.ApplicationClass' to class type 'WindowsApplication1.Access.ApplicationClass'. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface.
I don't know what to do.
You need to add the correct COM Reference. Click the Project Menu bar then-->Add reference. Under Type Libraries look for Microsoft Access 14.0 or whatever version your machine is running. Then add the following statements.
Imports Microsoft.Office.Interop
Dim Access_Object As New Access.Application
Access_Object = CreateObject("Access.Application")
Access_Object.Application.Visible = True

Why doesn't code work in VB.net, but works in VBA; GetObject

VBA code works great:
Sub testVBA()
Dim wb As Object ' Lotus123.Document
Set wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
VB.net code fails:
Sub TestVBNet()
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
In VB.net I get a FileNotFoundException: "File name or class name not found during Automation operation."
As I can run it from VBA that means the file exists and that the class name exists. So why doesn't it work and how can I fix it in VB.net.
EDIT: I guess I'm not sure how to start diagnosing this: Obviously the class exists on my computer but somehow VB.net doesn't manage to find it. Maybe VB.net uses a different method to activate the class. Maybe a registry entry is missing. I am glad for any suggestions.
Edit 2: I also tried using CreateObject and got this error: "Cannot create ActiveX component." Not unexpected.
For some reason VB.net cannot find the class name "Lotus123.Workbook" so I tried getting the file without the class name and it works fine in XP.
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3")
EDIT: In Win8 64bit the above doesn't work; just hangs.
The code below works in XP 32 bit as well as in Win8 64 bit. I checked with process monitor what is happening under the hood. CreateObject checks for the CLSID in the registry using the given object. Then it looks up the necessary info using the CLSID.
Public Shared Function GetLotusWB(ByVal sFile As String) As Object
'HKCU takes precedence if exists
'HKCU\Software\Classes\Lotus123.Workbook\CLSID
'HKCU\Software\Classes\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}
'normally this is used because Lotus123 doesn't create HKCU entries
'HKCR\Lotus123.Workbook\CLSID = {29130007-2EED-1069-BF5D-00DD011186B7}
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\InprocHandler32 = ole32.dll
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\LocalServer32 = C:\Lotus\123\123w.exe
'using object as that sometimes works better
Dim LotusObj As Object = CreateObject("Lotus123.Workbook")
'get application
'need a reference to Lotus 123 else declare as Object
Dim LotusApp As Lotus123.Application = LotusObj.Application
'FAILS: LotusApp.Visible = True
'open file; also works fine As Lotus123.Document
Dim ldoc As Object = LotusApp.OpenDocument(sFile)
'visible and activate (must declare as Object else gives exception)
Dim appObject As Object = ldoc.Application
appObject.Visible = True
ldoc.Activate()
Return ldoc
End Function
This works great because it creates the "Lotus123.Workbook" which is used to get the application object.
Load the file into an Excel workbook. It should be able to convert the lotus123 workbook on the fly.
First of all, check to make sure your inclusions (I think under Tools menu, includes or references or something like that) include the library that references Lotus123.Document. Chances are it's in the "Microsoft Excel 14.0 Object Library" or similar.
I've heard it said that VB is not VBA!

Application Object "Out Of Range" Error Initializing Legacy VB6 COM-Addin for Outlook 2013

I'm attempting to ensure compatibility for an old VB6 COM-addin for Outlook 2013. It's worked fine (more or less) for every version since 2002 but in testing 2013 I'm getting an odd error when I attempt to initialize.
As soon as I try to take ANY action on the Outlook Application object I blow up with a "09 - Subscript Out Of Range" error. Even something as benign as casting the the olApp parameter I'm bringing into the class as a private var blows up. The Application object isn't null, it's property rich and seemingly initialized.
Friend Sub InitHandler(olApp As Outlook.Application, strProgID As String)
On Error GoTo ErrorExit
'If Outlook is opened without main UI (i.e. 'mailto:' HTML link)
If Explorers.Count = 0 Then Exit Sub
' BLOWS UP SO TRIED COMMENTING IT OUT AND ACCESSING PARAMETER DIRECTLY
'Set objOutlook = olApp
' ALSO BLOWS UP
Set objNameSpace = olApp.GetNamespace("MAPI")
...
Have there been any major architectural changes to the object model? Any ideas?
The issue here was that I needed to install the VB6 service pack 6 and cumulative rollout update.