• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

_IMsoDispObj

Started by Frederick J. Harris, December 30, 2011, 08:11:55 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

So far I'm doing OK with my C++ header file generator program, and have successfully generated a number of headers for a number of typelibs.  Right now I'm working on Excel - which is a big one!  About the 1st 10 interfaces or so inherit from _IMsoDispObj.  Here is your interface description for it Jose...


INTERFACE Adjustments $IID_Adjustments

   INHERIT IDispatch

   ' =====================================================================================
   ' _IMsoDispObj Methods
   ' =====================================================================================
   PROPERTY GET Application <1610743808> ( _            ' VTable offset = 28
   ) AS IDispatch                                       ' [retval][out] **ppidisp VT_DISPATCH <IDispatch>
   ' =====================================================================================
   PROPERTY GET Creator <1610743809> ( _                ' VTable offset = 32
   ) AS LONG                                            ' [retval][out] *plCreator VT_I4 <Long>
   ' =====================================================================================

   ' =====================================================================================
   ' Adjustments Methods
   ' =====================================================================================
   PROPERTY GET Parent <1> ( _                          ' VTable offset = 36
   ) AS IDispatch                                       ' [retval][out] **Parent VT_DISPATCH <IDispatch>
   ' =====================================================================================
   PROPERTY GET Count <2> ( _                           ' VTable offset = 40
   ) AS LONG                                            ' [retval][out] *Count VT_INT <Long>
   ' =====================================================================================
   PROPERTY GET Item <0> ( _                            ' VTable offset = 44
     BYVAL prm_Index AS LONG _                          ' [in] Index VT_INT <Long>
   ) AS SINGLE                                          ' [retval][out] *Val VT_R4 <Single>
   ' =====================================================================================
   PROPERTY SET Item <0> ( _                            ' VTable offset = 48
     BYVAL prm_Index AS LONG _                          ' [in] Index VT_INT <Long>
   , BYVAL prm_Val AS SINGLE _                          ' [in] Val VT_R4 <Single>
   )                                                    ' void
   ' =====================================================================================

END INTERFACE


I looked it up on the internet and its apparently something related to Microsoft Office and .NET.  It contains the two methods you prepended to the interface.  I'm wondering if your code somehow sank deeper into its recursive decent with ITypeInfo::GetRefTypeOfImplType / ITypeInfo::GetRefTypeInfo to finally come up with the ultimate base of IDispatch, or if you just 'hard coded' that response into your code for any situation where _IMsoDispObj shows up?  The problem with it for me in dealing with C++ is that what my program is outputting won't compile because _MsoDispObj isn't predefined/declared anywhere before a C++ compiler would encounter it.  Here is what my program is outputting for the 1st one encountered in Excel...


interface Adjustments : _IMsoDispObj
{
virtual HRESULT __stdcall GetParent(IDispatch** Parent)=0;
virtual HRESULT __stdcall GetCount(int* Count)=0;
virtual HRESULT __stdcall GetItem(int Index, float* Val)=0;
virtual HRESULT __stdcall SetItem(int Index, float Val)=0;
};


If I changed it to IDispatch which is what you and PowerBASIC have, that would compile, because the system knows what 'IDispatch' is; but not _IMsoDispObj.  That's kind of why I'm asking if your code is dynamically resolving IDispatch from that, or is just inserting it in there?



Frederick J. Harris

I have to admit, I expect you are going to tell me that your code is recursively descending deeper until its coming up with IDispatch.  I recall you were using a recursive function.  However, I didn't implement my code for that specific functionality recursively.  Perhaps I'll have to change that if _IMsoDispObj can be decomposed further into IDispatch.  I'm am using recursion in several places; just not there.

José Roca

There is nothing hard coded. If the inherited interface is not IUnknown or IDispatch, I call the following function, that loads and parses the external type library where that interface is defined.


' ########################################################################################
' Enumerates external interfaces
' ########################################################################################
FUNCTION TLB_EnumExternalInterfaces ( _
   BYVAL hWnd                  AS DWORD _              ' // Handle of the main window
, BYVAL pITypeInfo            AS ITypeInfo _          ' // ITypeinfo interface
, BYVAL strItemName           AS STRING _             ' // Item to retrieve
, BYVAL fFlags                AS LONG _               ' // Flags
, BYVAL bIsUnknown            AS LONG _               ' // Is an IUnknwon interface
   ) AS LONG

   IF ISNOTHING(pITypeInfo) THEN EXIT FUNCTION
   IF strItemName = "" THEN EXIT FUNCTION

   LOCAL hr                    AS LONG                 ' // HRESULT
   LOCAL strImplInterface      AS STRING               ' // Implemented interface
   LOCAL strImplInterfaceIID   AS STRING               ' // Implemented interface IID
   LOCAL strTypeLibGuid        AS STRING               ' // Type library guid
   LOCAL strTypeLibVersion     AS STRING               ' // Type library version
   LOCAL wTypeLibVerMajor      AS WORD                 ' // Type library major version
   LOCAL wTypeLibVerMinor      AS WORD                 ' // Type library minor version
   LOCAL strTypeLibPath        AS STRING               ' // Type library path
   LOCAL wszTypeLibPath        AS WSTRINGZ * %MAX_PATH ' // Type library path (unicode)
   LOCAL pITypeLib             AS ITypeLib             ' // Pointer to ITypeLib interface
   LOCAL TypeInfoCount         AS LONG                 ' // Number of typeinfos
   LOCAL pExtTypeInfo          AS ITypeInfo            ' // Pointer to ITypeInfo interface
   LOCAL pExtTypeAttr          AS TYPEATTR PTR         ' // Pointer to TYPEATTR structure
   LOCAL bstrName              AS WSTRING              ' // Interface name
   LOCAL bstrDocString         AS WSTRING              ' // Documentation string
   LOCAL pdwHelpContext        AS DWORD                ' // Help context
   LOCAL bstrHelpFile          AS WSTRING              ' // Help file
   LOCAL szInterfaceName       AS ASCIIZ * 256         ' // Interface name
   LOCAL pTKind                AS DWORD                ' // Interface type
   LOCAL i                     AS LONG                 ' // Loop counter
   LOCAL pRefType              AS DWORD                ' // Reference type
   LOCAL pRefTypeInfo          AS ITypeInfo            ' // Pointer to ITypeInfo
   LOCAL pRefTypeAttr          AS TYPEATTR PTR         ' // Pointer to a TYPEATTR structure
   LOCAL strInheritedInterface AS STRING               ' // Inherited interface name
   LOCAL strNewInhInterface    AS STRING               ' // New inherited interface name
   LOCAL old_UsePropGetSet     AS LONG

   strImplInterface = UCASE$(TLB_GetImplementedInterface(pITypeInfo))
   IF strImplInterface <> "" AND strImplInterface <> "IUNKNOWN" AND strImplInterface <> "IDISPATCH" THEN
      strImplInterfaceIID = TLB_GetImplementedInterfaceIID(pITypeInfo)
      hr = TLB_GetTypeLibFromInterfaceIID(strImplInterfaceIID, strTypeLibGuid, strTypeLibVersion, wTypeLibVerMajor, wTypeLibVerMinor, strTypeLibPath)
      IF hr = %S_OK AND strTypeLibPath <> "" THEN
         wszTypeLibPath = strTypeLibPath
         hr = LoadTypeLibEx(wszTypeLibPath, %REGKIND_NONE, pITypeLib)
         IF hr <> %S_OK OR ISFALSE ISOBJECT(pITypeLib) THEN EXIT FUNCTION
         ' --- Retrieves the number of TypeInfos -----------------------------------------------
         TypeInfoCount = pITypeLib.GetTypeInfoCount
         IF TypeInfoCount = 0 THEN
            pITypeLib = NOTHING
            EXIT FUNCTION
         END IF
         ' --- Parses the TypeInfos ------------------------------------------------------------
         FOR i = 0 TO TypeInfoCount - 1
            ' --- Allow for a breath and check the abort flag ---------------------------------
            TLB_DoEvents hWnd
            IF m_Abort THEN EXIT FOR
            ' --- Retrieves the TypeKind ------------------------------------------------------
            hr = pITypeLib.GetTypeInfoType(i, pTKind)
            IF hr <> %S_OK THEN EXIT FOR
            ' --- Retrieves the TypeInfo interface --------------------------------------------
            hr = pITypeLib.GetTypeInfo(i, pExtTypeInfo)
            IF hr <> %S_OK THEN EXIT FOR
            ' --- Gets the address of a pointer to the TYPEATTR structure ---------------------
            hr = pExtTypeInfo.GetTypeAttr(pExtTypeAttr)
            IF hr <> %S_OK OR pExtTypeAttr = %NULL THEN EXIT FOR
            ' --- If it is an interface ... ---------------------------------------------------
            IF pTKind = %TKIND_INTERFACE OR pTKind = %TKIND_DISPATCH THEN
               hr = pITypeLib.GetDocumentation(i, bstrName, bstrDocString, pdwHelpContext, bstrHelpFile)
               ' -- If it is the interface we are looking for... ------------------------------
               szInterfaceName = bstrName
               IF szInterfaceName = strItemName THEN
                  IF (@pExtTypeAttr.wTypeFlags AND %TYPEFLAG_FDUAL) = %TYPEFLAG_FDUAL THEN
                     strInheritedInterface = TLB_GetInheritedInterface(pExtTypeInfo, -1)
                  ELSE
                     strInheritedInterface = TLB_GetImplementedInterface(pITypeInfo)
                  END IF
                  IF pTKind = %TKIND_DISPATCH THEN
                     ' Attempt to change the view to VTable
                     hr = pExtTypeInfo.GetRefTypeOfImplType(-1, pRefType)
                     IF hr = %S_OK AND pRefType <> %NULL THEN
                        hr = pExtTypeInfo.GetRefTypeInfo(pRefType, pRefTypeInfo)
                        IF hr = %S_OK AND ISTRUE ISOBJECT(pRefTypeInfo) THEN
                           hr = pRefTypeInfo.GetTypeAttr(pRefTypeAttr)
                           ' Enumerate the functions
                           IF hr <> %S_OK OR pRefTypeAttr = %NULL THEN
                              TLB_EnumFunctions(hWnd, pExtTypeAttr, pExtTypeInfo, 0, %TRUE, IIF&(bIsUnknown = %TRUE, %CODEGEN_CSTYLE, %CODEGEN_VBSTYLE), 0, bIsUnknown, %FALSE, szInterfaceName)
                           ELSE
                              IF strInheritedInterface <> "" AND UCASE$(strInheritedInterface) <> "IDISPATCH" AND UCASE$(strInheritedInterface) <> "IUNKNOWN" THEN
                                 strNewInhInterface = TLB_EnumInheritedInterfaces(hWnd, pITypeLib, strInheritedInterface, fFlags, bIsUnknown)
                                 ' If the returned interface name is the same, it must be an external interface
                                 IF strNewInhInterface = strInheritedInterface THEN
                                    TLB_EnumExternalInterfaces(hWnd, pRefTypeInfo, strInheritedInterface, fFlags, bIsUnknown)
                                 END IF
                              END IF
                              TLB_EnumFunctions(hWnd, pRefTypeAttr, pRefTypeInfo, 0, %TRUE, IIF&(bIsUnknown = %TRUE, %CODEGEN_CSTYLE, %CODEGEN_VBSTYLE), 0, bIsUnknown, %FALSE, szInterfaceName)
                              pRefTypeInfo.ReleaseTypeAttr(pRefTypeAttr)
                              pRefTypeAttr = %NULL
                           END IF
                        END IF
                     ELSE
                        TLB_EnumFunctions(hWnd, pExtTypeAttr, pExtTypeInfo, 0, %TRUE, IIF&(bIsUnknown = %TRUE, %CODEGEN_CSTYLE, %CODEGEN_VBSTYLE), 0, bIsUnknown, %FALSE, szInterfaceName)
                     END IF
                  ELSE
                     IF strInheritedInterface <> "" AND UCASE$(strInheritedInterface) <> "IDISPATCH" AND UCASE$(strInheritedInterface) <> "IUNKNOWN" THEN
                        strNewInhInterface = TLB_EnumInheritedInterfaces(hWnd, pITypeLib, strInheritedInterface, fFlags, bIsUnknown)
                        ' If the returned interface name is the same, it must be an external interface
                        IF strNewInhInterface = strInheritedInterface THEN
                           TLB_EnumExternalInterfaces(hWnd, pITypeInfo, strInheritedInterface, fFlags, bIsUnknown)
                        END IF
                     END IF
                     TLB_EnumFunctions(hWnd, pExtTypeAttr, pExtTypeInfo, 0, %TRUE, IIF&(bIsUnknown = %TRUE, %CODEGEN_CSTYLE, %CODEGEN_VBSTYLE), 0, bIsUnknown, %FALSE, szInterfaceName)
                  END IF
               END IF
            END IF
            IF ISTRUE pExtTypeAttr THEN
               pExtTypeInfo.ReleaseTypeAttr(pExtTypeAttr)
               pExtTypeAttr = %NULL
            END IF
            pExtTypeInfo = NOTHING
         NEXT
         pITypeLib = NOTHING
      END IF
   END IF

END FUNCTION


Of course, that function also calls other functions, among them the following one, that retrieves the path of the type library to load.


' ========================================================================================
' Returns the guid of the typelib that implements a given interface.
' strIID is the human readable guid of the interface.
' ========================================================================================
FUNCTION TLB_GetTypeLibFromInterfaceIID ( _
   BYVAL strIID            AS STRING _
, BYREF strTypeLibGuid    AS STRING _
, BYREF strTypelibVersion AS STRING _
, BYREF wTypeLibVerMajor  AS WORD _
, BYREF wTypeLibVerMinor  AS WORD _
, BYREF strTypeLibPath    AS STRING _
   ) AS LONG

   LOCAL szKey           AS ASCIIZ * %MAX_PATH
   LOCAL hKey            AS DWORD
   LOCAL dwIdx           AS DWORD
   LOCAL hr              AS LONG
   LOCAL szValueName     AS ASCIIZ * %MAX_PATH
   LOCAL KeyType         AS DWORD
   LOCAL szKeyValue      AS ASCIIZ * %MAX_PATH
   LOCAL cbValueName     AS DWORD
   LOCAL cbData          AS DWORD
   LOCAL p               AS LONG
   LOCAL rTypeLibGuid    AS GUID
   LOCAL bstrTypeLibPath AS WSTRING

   ' Searches the HKEY_CLASSES_ROOT\Interface\<IID>\Typelib node.
   szKey = "Interface\" & strIID & "\TypeLib"
   hr = RegOpenKeyEx (%HKEY_CLASSES_ROOT, szKey, 0, %KEY_READ, hKey)
   IF hr <> %ERROR_SUCCESS THEN
      FUNCTION = hr
      EXIT FUNCTION
   END IF

   ' Retrieves the Guid of the TypeLib
   dwIdx = 0
   cbValueName = %MAX_PATH
   cbData = %MAX_PATH
   KeyType = %REG_SZ
   hr = RegEnumValue (hKey, dwIdx, szValueName, cbValueName, BYVAL %NULL, KeyType, szKeyValue, cbData)
   strTypeLibGuid = szKeyValue
   rTypeLibGuid = GUID$(strTypeLibGuid)

   ' Retrieves the version
   DO
      dwIdx = dwIdx + 1
      cbValueName = %MAX_PATH
      cbData = %MAX_PATH
      KeyType = %REG_SZ
      szValueName = ""
      hr = RegEnumValue (hKey, dwIdx, szValueName, cbValueName, BYVAL %NULL, KeyType, szKeyValue, cbData)
      IF hr <> %ERROR_SUCCESS THEN EXIT DO
      IF UCASE$(szValueName) = "VERSION" THEN
         strTypeLibVersion = szKeyValue
         p = INSTR(strTypeLibVersion, ".")
         IF p = 0 THEN
            wTypelibVerMajor = VAL(strTypeLibVersion)
         ELSE
            wTypeLibVerMajor = VAL(LEFT$(strTypeLibVersion, p - 1))
            wTypelibVerMinor = VAL(MID$(strTypeLibVersion, p + 1))
         END IF
         EXIT DO
      END IF
   LOOP

   ' Closes the registry
   RegCloseKey hKey

   ' Retrieves the path of the TypeLib
   hr = QueryPathOfRegTypeLib(rTypeLibGuid, wTypeLibVerMajor, wTypeLibVerMinor, 0, bstrTypeLibPath)
   strTypeLibPath = bstrTypeLibPath

END FUNCTION
' ========================================================================================


Frederick J. Harris

Wow!  I actually thought of that for a fleeting moment when my internet search revealed the external Office file containing _IMsoDispObj, but I quickly disgarded the idea as being unlikely.  What I find startling is that so very much of the information is ridiculously easy to get, and a very, very small part - such as the base class for an interface, is so unbelievably difficult to get!

Frederick J. Harris

Thanks very much Jose.  This is certainly a complication.  Now I'll have to study and assimilate this. 

José Roca

And the worst thing is that the result will be unusable, since Excel (and Word) can only be used calling the Invoke method of the IDispatch interface.

Frederick J. Harris

You know, I had really wondered about that, because a great many of the most commonly used interfaces of Excel, such as WorkBook, WorkSheet, etc., are defined as IDispatch dual interfaces?  I seem to vaguely recall you stating at some time in the past that there was some kind of bug or internal problem regarding this.  In any case, it appears it was Microsoft's intent to provide VTable access.  IDispatch::Invoke in C++ is a nightmare.

Frederick J. Harris

Just the other night I had picked up a book I have on COM by Don Box - a noted COM authority.  I was reading his last chapter about the evolution of OOP thinking, and somehow he got into mentioning Excel, and that Microsoft had to do some esoteric and strange stuff to not bloat the memory requirements too much, such as using 'tear off' interfaces, so on and so forth.  Well, it looks like they failed on every count.  Not only doesn't it work, but its a bloated pig anyhow!

Dominic Mitchell

My Type Library Browser generates three files for Microsoft Excel.
As a result, I don't have to go hunting for the _IMsoDispObj dual interface.

Excel.inc - Microsoft Excel Object Library
Office.inc - Microsoft Office Object Library
VBIDE.inc - Microsoft Visual Basic for Applications Extensibility
Dominic Mitchell
Phoenix Visual Designer
http://www.phnxthunder.com

Frederick J. Harris

#9
Interesting idea Dominic. 

I'm wondering just how common this situation is with Type Libraries.  Since I'm exceedingly new to this I've only tried to create headers for several type libs so far, specifically, Ado, MSFlexGrid, Shell.Explorer, and now Excel.  On each I've had to tweak my code to deal with new things encountered (particularly how things are ordered). 

I'd like to handle this in the simplest but adequate manner.  Is Microsoft Office the only place this is encountered I wonder?  Jose's solution requires zero a priori knowledge concerning a type library.  The solution you mentioned requires a priori knowledge that some particular typelib/vendor is associated with a specific series of other files.     

Frederick J. Harris

Quote
...since Excel (and Word) can only be used calling the Invoke method of the IDispatch interface.

I thought sometime soon after PB9 came out I saw posts in the PowerBASIC forums showing how to use direct interface calls with Word?  I have lots of production PB7 - 8 code doing IDBind dispatch calls with Word, but was thinking of testing with direct interface calls when I'd get around to it (which seems like never I guess).  Am I wrong about that?

Dominic Mitchell

Quote
I have lots of production PB7 - 8 code doing IDBind dispatch calls with Word, but was thinking
of testing with direct interface calls when I'd get around to it (which seems like never I guess).
Am I wrong about that?
My advice is to use automation with Excel and Word. Direct interface calls with these products
is a gamble that fails more often than not.

A case in point is opening a *.xls file in Excel and then using the VLookup method.
Automation works flawlessly, the direct inteface approach blows up around the time the
Worksheetfunction object is retrieved.
Dominic Mitchell
Phoenix Visual Designer
http://www.phnxthunder.com

José Roca

#12
There are methods that return a pointer to a CoClass instead of an interface. When you try to use this pointer... boom!

Dominic Mitchell

It is more common than you think.

For example, for the Microsoft FlexGrid Control the PowerBASIC COM browser generates this
 
Member Get Font <-512> () As IDispatch
Member Get Picture <49> () As IDispatch

which should actually be this

Member Get Font <-512> () As StdFont
Member Get Picture <49> () As StdPicture

But this requires generating a header for the external reference stdold2.tlb.
There is nothing wrong with the first approach. However, the poor soul wanting to use
the external object would have to figure out what it is and where it is located.
Dominic Mitchell
Phoenix Visual Designer
http://www.phnxthunder.com

Frederick J. Harris

Quote
There are methods that return a pointer to a CoClass instead of an interface. When you try to use this pointer... boom!

I've been struggling to wrap my head around that one as I've been seeing a fair amount of that.  So far I can't grasp it.  One of the fundamental ideas of COM it seems to me is to not give access to CoClasses, but rather to interfaces.  Granted, for the 1st interface pointer in a CoClass the addresses will be the same for both the CoClass and the interface, but nonetheless, why return a pointer marked as a pointer to a CoClass instead of to an interface?  Right now I'm just looking the other way on that one, and just trying to get my auto-generated code to compile and run.