I am facing difficulty in understanding the following concepts. I had posted a question some time back - read through the answers but some things are still not clear. I state my confusion below:
My first question refers to the following code piece
Option Strict On
Imports Microsoft.Office.Interop
Dim oxl As Excel.Application
oxl = CreateObject("Excel.Application")
In the above code piece, the statement oxl = CreateObject("Excel.Application") throws an error stating, Option Strict On disallows implicit conversions from Object to Application. My question is I read from many sources that it is always better to keep Option Strict ON but in this case when we need to create a new excel application, the Option Strict ON is preventing us from doing so. So what is the best practice that should be followed for such a conflict?
Next I tried replacing the statement oxl = CreateObject("Excel.Application") with oxl = New Excel.Application. It was observed that even with Option Strict ON, we can create a new excel application object with the NEW keyword. It was also checked with GetType that in both cases that is, using CreateObject and NEW, the type of object being created was: System._ComObject.So my question is if the type of object being created remains remains the same, why is that Option Strict disallows CreateObject but allows the creation of the excel application object using NEW?
To study it further, I extended the above code to the following:
Option Strict On
Imports System
Imports Microsoft.Office.Interop
Module Program
Dim oxl As Excel.Application
Dim owb As Excel.Workbook
Dim osheet As Excel.Worksheet
Sub Main()
oxl = New Excel.Application
'oxl = CreateObject("Excel.Application")
Console.WriteLine(oxl.GetType)
oxl.Visible = True
owb = oxl.Workbooks.Add()
osheet = owb.Worksheets("Sheet1") ‘Error: Option Strict ON disallows implicit conversions from ‘Object’ to ‘Worksheet’
osheet.Range("A1").Value = 53
Console.WriteLine("Hello World!")
Console.ReadLine()
End Sub
End Module
When we run the code we see that the error Option Strict ON disallows implicit conversions from ‘Object’ to ‘Worksheet’ comes at the line: osheet = owb.Worksheets("Sheet1")
Question:
Why is the error coming? I mean if, owb = oxl.Workbooks.Add()can work (that it returns a workbook which is referred to by owb) then why is osheet = owb.Worksheets("Sheet1") not working because the right hand side returns the “Sheet1” of the workbook which osheet should be able to point to (given that it is of the type Excel.Worksheet)?
Sometimes it just doesn't have the information that something is more specific than an Object. If you don't use the default property and use Item instead, as in owb.Worksheets.Item("Sheet1"), you can hover over the Worksheets part to see that represents the .Sheets, but hovering over the Item part reveals it has no details of the items therein - it says it returns an Object.
You know what it should be, so if you had
Imports XL = Microsoft.Office.Interop.Excel
then you could do
osheet = DirectCast(owb.Worksheets("Sheet1"), XL.Worksheet)
and the types would all work out.
This is what VB statements about COM objects actually do.
Information for Visual Basic Programmers
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
A standard COM VTable the first three entries are IUnknown
AddRef, Release (decreases the ref count), and QueryInterface to find what interfaces this object support.
The next four entries are IDispatch
GetIDsOfNames, Invoke , GetTypeInfoCount, GetTypeInfo.
The entries after that are your methods and properties, and all are a indirect function call.
To get the code in memory you use the COM API calls, such as CoCreateInstance
You need to decide to early or late bind. Early binding requires the program to be installed so its type library can be read so it types are compiled into the program. Late binding doesn't care about compile time. There is a conversation Hello object, do you have a function called x. Object replies Yes, it is function 7, Can you please do function 7 object. Early binding function 7 is hard coded. You can only late bind to generic objects. –
So a COM object is 4 x 32 bit. One is the reference count, one is the address if the Virtual Function Table (VTable), 2 are unused. In early binding to call a function 7 the compiler does Address_Of_Vtable + (4 x 7) (being 4 bytes for an address). See IDispatch. N.B. Microsoft.Office.Interop is not used at all in late binding. –
Only the generic object can be used in late binding and cannot be used in early binding. Early binding requires you to tell it the specific object. You are mixing and matching. The compiler is confused, just like you. –
Option Strict Restricts implicit data type conversions to only widening conversions, disallows late binding, and disallows implicit typing that results in an Object type. learn.microsoft.com/en-us/dotnet/visual-basic/… –
Related
I am trying to write a function which will tell if Excel VBA has a reference set to Outlook:
Function RefOutlook() As Boolean
Application.Volatile
On Error Resume Next
If olFolderInbox=6 Then
RefOutlook = True
Else
RefOutlook = False
End If
End Function
My logic is that if a reference is established, VBA will recognise an Outlook built-in constant olFolderInbox (equal to 6) and will return True, otherwise there will be an error and the function shall return False. The problem is that if there is no reference, the error is not suppressed, an error message comes up and the function is not working.
Many thanks in advance for your feedback and suggestions.
What you really need is to specify Option Strict Statement in the beginning of VBA modules. Restricts implicit data type conversions to only widening conversions, disallows late binding, and disallows implicit typing that results in an Object type.
When Option Strict On or Option Strict appears in a file, the following conditions cause a compile-time error:
Implicit narrowing conversions
Late binding
Implicit typing that results in an Object type
So, you will get a compile-time error if the Outlook reference is not added.
I would suggest that you use early binding for development, and convert to late binding when deploying your application. By doing so, you not only avoid the problem of constants not being available, but also any versioning issues.
When you do so, you will need to replace any constants, such as olFolderInbox, with your own global constants:
Global Const OL_FOLDER_INBOX = 1
Regards,
This question already has answers here:
How to use GetObject in VBScript
(2 answers)
Closed 3 years ago.
I recently got a small CG software package from my friend his father wrote about 30 years ago. It has a window where one can create and render some solid primitives. Interestingly, it has a VBA interface and I can in Excel (let’s call it TheSoftware) use Set app = CreateObject(“TheSoftware.Application”) to create such application (after setting app.Visible to True one can get the window on the screen)! However, the GetObject(, “TheSoftware.Application”) returns the “ActiveX component can’t create object” error. More surprisingly to me, the GetObject(“”, “TheSoftware.Application”) successfully creates the application! I have its source code in C++ about 900 KB that I can share, but just to be quick, has anyone had this issue before, or have a clue what may be the issue? I am no expert in C++, and the author passed away 4 years ago. I have seen the “opposite” problem of failing to create an object but OK when getting it, but not this one. Any idea is welcome, thanks!
GetObject(FName) opens a file or connects to a file if it already opened (and if class is specified use that class to open it) OR GetObject(,"Appname") connects to a running instance of an application. You are creating a blank file.
Visual Basic Scripting Edition
GetObject Function
See Also CreateObject Function
Requirements
Version 5
Returns a reference to an Automation object from a file.
GetObject([pathname] [, class])
Arguments
pathname
Optional; String. Full path and name of the file containing the object to retrieve.
If pathname is omitted, class is required.
class
Optional; String. Class of the object.
The class argument uses the syntax appname.objectype and has these
parts:
Use the GetObject function to access an Automation object from a file
and assign the object to an object variable. Use the Set statement to
assign the object returned by GetObject to the object variable. For
example:
Dim CADObject
Set CADObject = GetObject("C:\CAD\SCHEMA.CAD")
When this code is executed, the application associated with the
specified pathname is started and the object in the specified file is
activated. If pathname is a zero-length string (""), GetObject
returns a new object instance of the specified type. If the pathname
argument is omitted, GetObject returns a currently active object of
the specified type. If no object of the specified type exists, an
error occurs.
Some applications allow you to activate part of a file. Add an
exclamation point (!) to the end of the file name and follow it with a
string that identifies the part of the file you want to activate. For
information on how to create this string, see the documentation for
the application that created the object.
For example, in a drawing application you might have multiple layers
to a drawing stored in a file. You could use the following code to
activate a layer within a drawing called SCHEMA.CAD:
Set LayerObject = GetObject("C:\CAD\SCHEMA.CAD!Layer3")
If you don't specify the object's class, Automation determines the application to start and the object to activate, based on the file
name you provide. Some files, however, may support more than one class
of object. For example, a drawing might support three different types
of objects: an Application object, a Drawing object, and a Toolbar
object, all of which are part of the same file. To specify which
object in a file you want to activate, use the optional class
argument. For example:
Dim MyObject
Set MyObject = GetObject("C:\DRAWINGS\SAMPLE.DRW", "FIGMENT.DRAWING")
In the preceding example, FIGMENT is the name of a drawing application
and DRAWING is one of the object types it supports. Once an object is
activated, you reference it in code using the object variable you
defined. In the preceding example, you access properties and methods
of the new object using the object variable MyObject. For example:
MyObject.Line 9, 90
MyObject.InsertText 9, 100, "Hello, world."
MyObject.SaveAs "C:\DRAWINGS\SAMPLE.DRW"
Note Use the GetObject function when there is a current instance
of the object or if you want to create the object with a file already
loaded. If there is no current instance, and you don't want the
object started with a file loaded, use the CreateObject function.
If an object has registered itself as a single-instance object, only
one instance of the object is created, no matter how many times
CreateObject is executed. With a single-instance object, GetObject
always returns the same instance when called with the zero-length
string ("") syntax, and it causes an error if the pathname argument is
omitted.
Requirements Version 5
See Also CreateObject Function
© 2001 Microsoft Corporation. All rights reserved.
Build: Topic Version 5.6.9309.1546
Also these are the COM API calls that each form makes. From the Automation part of the COM docs.
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
I am writing a VBA application, and for a specific function, I am using only late-binding, as most of the users of the application won't have the reference installed (and won't use this specific function).
The object I am using behaves like:
class PISDK{
PIServer GetServer(string hostName)
}
The GetServer method returns a PIServer object, but a more specific interface exists, implementing PIServer:
interface IGetPoints2 : PIServer{}
I would like to downcast the PIServer object to a IGetPoints2 object.
Without doing anything, I get a PIServer object:
Dim PiSdk As Object
Dim PiServer As Object
Set PiSdk = CreateObject("PISDK.PISDK")
Set PiServer = PiSdk.GetServer("foo")
Looking at PiServer in the debugger confirms that.
Using a strongly typed variable should work, but I do not want to reference any of the types used here.
How can I downcast this object using late-binding only?
Please read this:
As you are not adding a reference to the PI SDK Type Library, I
believe you cannot use "rtInterpolated" as the second parameter of
the ArcValue method; instead, you can use the corresponding number
(which is 3 for "rtInterpolated" in the RetrievalTypeConstants
enumeration).
VBA with late binding is tricky with optional
parameters, as we cannot omit them when calling a method. Instead,
you need to use either Nothing (in case the optional parameter is
an object) or "" (in case the optional parameter is a string) as
"parameter placeholders" (by the way, the same happens in
VBScript, a scripting language for scripts contained in files with
.vbs extension that run independently from any application).
able to put some functional sample code together, which is more
complete and will hopefully help you.
I've created a spreadsheet which optionally uses Bloomberg data pulled using the API COM 3.5 Type Library. I want to be able to distribute that spreadsheet to non-Bloomberg users, but they can't run it since they don't have the right libraries.
All blpapi-related code besides what's in the Class Module is behind if statements that should not be entered by the non-BB users. In the class module, I lazily define the session and Service so that the blpapi-specific definitions are delayed until the class initializes (see below):
Option Explicit
Private session As Object
Dim refdataservice As Object
Private Sub Class_Initialize()
' First create session.
Set session = New blpapicomLib2.session
session.QueueEvents = True
session.Start
' Then open service.
' A service provides the schemas needed to make requests.
session.OpenService "//blp/refdata"
Set refdataservice = session.GetService("//blp/refdata")
End Sub
In short - the code which appears to be causing the issues never runs. My (very limited) understanding of VBA is mostly functional, so I'm probably missing something obvious. Is this a compilation-related error? Is there a way for me to precompile the VBA so users don't experience this issue? Maybe some type of error handling method so the workbook doesn't hang?
Here was my solution for my own problem:
I had two functions with inputs that used library-specific types. I converted those to generic objects (using late binding on the session and refdataservice was not enough). Then, I unselected the bbcom library, and added a dynamic reference to the dll file in Workbook_Open(). Even if the load fails, the error is able to be caught, whereas before excel would have to be killed. This is still a problem if the user tries to use part of the workbook that uses the BB-related code, but this can be mitigated in a few different ways.
Lately I've bumped into a question that made me pounder; it kept me busy and I couldn't find a transparent explanation for it on the net.
It is related to the destruction of Excel objects (which I use all the time and never really questioned before).
Background leading to my question:
With regular objects, you can instantiate an object using the keywords SET and NEW.
For example:
Set classInstance = New className
Whenever we instantiate this way, the object is created in the heap memory and the reference counter is increased by 1.
In case I don't add more references, the following statement would bring the reference count back to zero:
Set classInstance = Nothing
When the reference count goes to 0, the object is destroyed and cleared from memory and the "classInstance" points to .
What I've read:
When we use the "CREATEOBJECT" function, it returns a reference to a COM object.
Set oApp = CreateObject("Excel.Application")
Even though we could say:
Set oApp = nothing
The objects' reference count will go to 0, and oApp will not point to the object anymore.
My questions:
1) Why is it that this type of object requires to call the method .Quit before the object is actually being removed from memory?
The same goes when adding a reference to a workbook object (workbooks.add or workbook.open) which requires the .close method.
Why can't these objects be automatically destroyed when bringing the reference count to zero?
Which is the case when we say for example:
set oRange = nothing
2) And is there a need to say:
oApp.Quit
set oApp = nothing
Since the Application object is already cleared from memory when applying .Quit, there is no object to be released anymore.
The only reason I could come up with, why oApp would be set to Nothing after Quit, would be because it could be pointing to an unused memory location (on the heap) and could lead to confusion later if this memory would be re-assigned (although in VBA I find this hard to imagine).
I was questioning myself if this conclusion is correct and I would like to receive confirmation for that from someone who knows the answer.
Please, tell me if I see this wrongly.
3) What they call in VBA "a reference to an object" (such as oApp in the code above), I see them as pointer variables in C.
Would it be safe to use this statement or again, am I seeing this wrongly?
Generally is not hard to apply .Quit and set to nothing, but it would be nice to receive some accurate information on the topic. So that I know for 100% percent why I am doing it.
Good Question :)
Excel controls the creation of its objects. Likewise it also controls their destruction.
Setting oApp = Nothing just destroys the object reference. It doesn't remove the Application. To destroy an Excel object, you have to use it's .Quit method.
Whenever you do, Set x = Nothing, the reference(pointer) named x to its relevant object is removed. This doesn't mean that the object itself will be removed from the memory.
Whether the object will be removed from memory or not, depends on various factors.
Whether there are more references pointing towards the same object. If there are, the object will not be removed. The reference count must be zero.
The internal implementation of the destructor of that object.
The .Quit method is defined to graciously remove all the memory objects excel has allocated, and close itself.
It is similar to calling Close on a form in VB6. Take for example, a form in vb6.
Dim f As Form
Set f = Form1
f.Show
'
'~~> Rest of the code
'
Set f = Nothing
Will this destroy the form? :)
FOLLOWUP
How about question 2? Thanks – Kim Gysen 14 mins ago
It might not be exactly as shown here, and compiler optimizations may make things behave differently... but this is the basic concept that is at work.
Part 2 of your question is quite interesting, and it's well worth an extended answer.
This is going to cover three key points:Objects and object variables;Pitfalls when dismissing objects;...And an important change in reference-counting the Application object in Excel 2013.
But, if you want a short answer, it's: "Not all objects are equal".
Now read on...
Some objects are created in your Excel session's 'own' memory space, and their memory allocation is controlled by your session; some objects have persistent components that exist after the object variable is dismissed; and some do not:
Set oDict = CreateObject("Scripting.Dictionary")
Set oWShell = CreateObject("Shell.Application")
In both these cases, memory is allocated, the object variables (and their vTable of pointers to methods and properties) are instantiated, and they are yours to command until you dismiss them:
Set oDict = Nothing
Set oWShell = Nothing
And, on dismissal, no trace of them remains.
But this object is persistent:
Dim oWbk as Excel.Workbook
Set oWbk = Application.Workbooks.Add
...You've created a new workbook object and, if you dismiss the object variable with Set oWbk = Nothing, you will see that the new workbook object still exists as a visible presence in the user interface.
What you actually created was a Workbook object - a workbook window with an active worksheet and the full user interface that goes with that - and a Workbook object variable - a programmer's COM interface, a table of methods and properties for the Workbook object - that you can manipulate in code using the named entity 'oWbk'.
Dismissing the oWbk object variable removes that framework, but the Workbook itself will still exist: you've created a Workbook object, and it's yours to keep.
The object is more than its object variable and dismissing the variable does not destroy the object: it just dismisses an interface, a framework of methods and properties that you can use to manipulate the object in code.
Closing the Workbook, with or without saving a file, should automatically dismiss the object variable and clear up the memory allocated for that interface of properties, methods and attributes:
'try this:
oWbk.Close SaveChanges:=False
' or maybe this:
Application.Workbooks(Application.Workbooks.Count).Close SaveChanges:=False
...That is to say, you would expect both of those commands to call Set oWbk= Nothing - especially the oWbk.Close command - but if you try either of them without explicitly dismissing oWbk, you will find that oWbk still exists as an empty husk, and all calls and requests for information on it (try> Debug.Print> TypeName(oWbk) ) will return 'Automation Error' messages.
Some of the commments in the previous answer mention the UserForm object which - unlike the Dictionary and the Shell object - is an object with a visible user interface. But this user interface is not a persistent new object in the Excel user interface like a Workbook or a worksheet.
Luckily for you, the object you created is owned by your Excel session, and you can instantiate an object variable again, to get the same framework of methods and properties, and take control of the object again:
Set oWbk = Application.Workbooks(Application.Workbooks.Count)
...Assuming, of course, that you have some way of being sure that you identified the right workbook object: but that's not your question at all.
Where this answer is going is: objects that are not created in your Excel session's 'own' memory.
Set oApp = CreateObject("Excel.Application")
This statement will create an Excel object which, like the new Workbook, has a User Interface (although you'll need to set the .Visible property True to see it) and and a persistent presence in memory: once again, the object is more than its object variable, and dismising the variable does not destroy the object.
Unlike the new Workbook, it isn't quite yours to command: it's an Excel session in it's own right, it allocates its own memory - oApp's 'footprint' in your current session's memory is just the pointer and the name: the interface (vTable, iDispatch, and all those named methods with pointers to the structures that implement the arcane act of manipulating an Excel session in VBA) exists in the block of memory allocated by this new Excel session.
Here's what happens in Office 2010, and older versions of Excel:
Dismissing the object variable with Set oApp = Nothing leaves that session up and running, and I would strongly suggest that you make the session visible so that you can close it manually!
Closing that Excel session manually, without explicitly dismissing the oApp object variable, will definitely leave oApp in the 'empty husk' state, and a grim and headless spectre wailing 'The Automation object has disconnected from its clients!' in the dark corners of your code base.
But, in Office 2013 and later versions, Set oApp = Nothing performs exactly the reference-counting you would expect and the session closes. Try it:
Private Sub Test()
Dim oApp As Excel.Application
Set oApp = New Excel.Application
'Set oApp = CreateObject("Excel.Application")
oApp.Visible = True
Set oApp = Nothing
End Sub
It won't close on Set oApp = Nothing if another object variable has a reference - and that's not the only entity that gets to increment the reference counter: user activity in the GUI (try creating a new workbook and editing it) keeps the session up and running, too.
For your own amusement, see if oApp.Quit does actually dismiss oApp and sets it to Nothing.
Of course, oApp.Quit will definitely close the session...
...Or will it? If there is something going on in that session - a long calculation, or an 'modal' error message that you have to see and click before the Application object responds to any other input, from the user interface or your VBA - then oApp.Quit won't close the session.
Lets not go there. All things being equal, oApp.Quit will definitely close the session in 2010 and earlier versions of Excel.
But in Office 2013, calling 'Quit' from the last object variable merely hides the UI: the object variable still responds to your code - the methods and properties that don't require an active workbook are still accessible via oApp - and a separate instance of Excel.exe is clearly visible in the Processes tab of Task manager.
Likewise, quitting the new session by clicking the 'close' button in the user interface closes the session's windows but, if there's an object variable with a reference to this application object in your code, it's still there, in memory, and 'oApp' can still get at the properties and methods.
So the reference counter works both ways in current versions of Excel: the object exists until the reference count decrements to zero, and the last remaining object variable will not be left 'disconnected' by a quit command or UI action.
Nevertheless, your session doesn't 'own' that new application object: if you've dismissed the last object variable and set it to Nothing, and there's something else keeping the neww session alive - user activity, or some internal process - there's nothing like the Application.Workbooks() or Worksheets() collection to identify other Excel sessions and instantiate an object variable pointing to a specific instance of an Excel.Application object.
There are ways of getting a specific session using API calls, but they aren't as reliable as you might wish them to be.
...So, all in all, there's quite a lot in that 'part 2'.