0

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)?

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
Sougata
  • 319
  • 1
  • 10
  • 3
    [Early and Late Binding (Visual Basic)](https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/early-late-binding/) -- [Using early binding and late binding in Automation](https://learn.microsoft.com/en-us/previous-versions/office/troubleshoot/office-developer/binding-type-available-to-automation-clients) – Jimi Oct 22 '22 at 20:24
  • You need to decide to early or late bind. Early binding requires the program to be installed so it's 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. – Lundt Oct 22 '22 at 20:54
  • 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 he compiler does `Address_Of_Vtable + (4 x 7)` (being 4 bytes for an address). See IDispatch https://en.wikipedia.org/wiki/IDispatch. NB `Microsoft.Office.Interop` is not used at all in late binding. – Lundt Oct 22 '22 at 21:01
  • 1
    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. – Lundt Oct 22 '22 at 21:09
  • `Option Strict` *Restricts implicit data type conversions to only widening conversions, **disallows late binding**, and disallows implicit typing that results in an Object type.* https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/option-strict-statement – Lundt Oct 22 '22 at 22:06
  • @Jimi The article is really helpful...thanks for sharing the link – Sougata Oct 24 '22 at 05:54

2 Answers2

0

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/… –

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Lundt
  • 142
  • 1
  • 1
  • 3
  • 2
    "a COM object is 4 x 32 bit" is not true. It depends on how the COM object is implemented. You can only rely on the IUnknown reference to use it not on its implementation. VB also supports IUnknown (early binding) only interfaces, IDispatch (aka COM automation, late binding) is not mandatory. Some objects are "dual", meaning they support both IDispatch and IUnknown-derived equivalent interfaces, so you can use them in early or late binding. Also the term "generic object" has no meaning in COM nor Automation terms. – Simon Mourier Oct 23 '22 at 09:00
  • No to consumers of COM objects they get a pointer to a 16 byte structure. That is COM. *Object 4 bytes Any Object reference* - a pointer to an a 16 byte object. – Lundt Oct 23 '22 at 09:09
  • @SimonMourier ***Object** 4 bytes Any Object reference* from VBA Data type summary. https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/data-type-summary **Give an example where a COM object is not 4 integers.** – Lundt Oct 23 '22 at 20:23
  • With the exception of vanilla C, the compiler provides the code for IUnknown and IDispatch. To make an interface not dual requires you to uncheck a box in options. A programmer only writes code for their function calls. – Lundt Oct 24 '22 at 04:30
  • 2
    Lots of confusion in what you say. VB/VBA is not the same as VB.NET. The VBA Object reference you link is 4 bytes (with VB in 32bit, would be 8 bytes in Office VBA 64bit) because it's just a memory pointer, and it's not the same as saying "A COM object is 4 x 32bit", which is 16 bytes. And lastly, a COM object is not necessarily written in C nor C++. One can develop a COM object in any language. Plus you're very aggressive while I was just trying to educate. End of discussion. – Simon Mourier Oct 24 '22 at 06:27
  • @SimonMourier There is no confusion. I know how it works. Notice how everything I said was qualified to COM. I read the COM standard once a year for the last 20 years. – Lundt Oct 24 '22 at 06:57
  • Every version of Basic support obsolete keywords from earlier version to allow porting of code. If you don't understand the technicalities why are you commenting? `Call` changes the calling convention. No need for that. An object is a pointer on the stack, but 16 bytes in the heap. – Lundt Oct 24 '22 at 07:26
  • Are you talking about the VB keyword `Call`? – Craig Oct 25 '22 at 13:16
0

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.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
  • Thank you for sharing the `ITEM` line of thinking. However, one question. The first is regarding the casting operator that you have suggested. If I write, `oxl = New Excel.Application` I do not need any casting operator like in the case of `oxl = CType(CreateObject("Excel.Application"), Excel.Application)`. Why is this difference? That is Option Strict ON does not prevent `oxl` which is a variable of `Excel.Application` type to point towards the excel application object when it is being created using NEW but throws an error otherwise? – Sougata Oct 24 '22 at 09:13
  • Also, do you think that answer to the above question (in the comment) requires a very detailed COM knowledge/understanding? If that be the case then you may point that out. My knowledge about internal workings of COM is near zero and if you feel that it is better for me to accept certain points like these and move on, you may tell me so. However, if you can offer an easy explanation then that would be very very helpful. – Sougata Oct 24 '22 at 09:19
  • 1
    @Sougata I don't think you need to have a deep knowledge on the internal workings of COM. You do need to have a basic idea of early binding vs late binding. – Craig Oct 24 '22 at 13:57
  • @Craig Thanks for clarifying. But I do not understand (pointed out by others also) why "binding" becomes important here. The error displayed for a statement like `oxl = CreateObject("Excel.Application")` with Option Strict ON is that implicit conversion is not allowed. The way I explain it to myself is that we are creating a `System._ComObject ` which at compile time is treated as `OBJECT`. On the other hand, `oxl` the object variable is of `Excel.Application` type which points to an `Object` type - this is an implicit narrowing conversion which Option Strict disallows. Now coming to the ... – Sougata Oct 25 '22 at 07:56
  • binding part, irrespective of whether I use CreateObject or New to create an excel application object, they are cases of early binding because `oxl` is defined to be of `Excel.Application` type and not `Object` type. To summarize, both `oxl = New Excel.Application` and `oxl = CreateObject("Excel.Application") ` create the same object (with type `System._ComObject`). Also, in both cases we have early binding. So then, with Option Strict ON, why does the compiler not give the `disallows implicit conversions from Object to Application` error in case of NEW which we get for CreateObject? – Sougata Oct 25 '22 at 08:08
  • 1
    @Sougata I think you're getting confused by some of the internal machinery of how .NET implements interop with COM. `Excel.Application` is may appear as `System._ComObject` at runtime, but from the compiler's point of view, it is a unique type with a known interface. You can do `oxl = New Excel.Application` because `oxl` was declared as `Excel.Application` and your new expression has the same type. You cannot assign the result of `CreateObject` without a cast because `CreateObject` has a static type of `Object`. – Craig Oct 25 '22 at 13:21