Powerbasic Museum 2020-B

Webmaster: José Roca (PBWIN 10+/PBCC 6+) (SDK Forum) => Discussion => Topic started by: Frederick J. Harris on December 30, 2011, 08:11:55 PM

Title: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 08:11:55 PM
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?


Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 08:40:16 PM
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.
Title: Re: _IMsoDispObj
Post by: José Roca on December 30, 2011, 08:45:38 PM
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
' ========================================================================================

Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 08:55:00 PM
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!
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 09:04:55 PM
Thanks very much Jose.  This is certainly a complication.  Now I'll have to study and assimilate this. 
Title: Re: _IMsoDispObj
Post by: José Roca on December 30, 2011, 09:37:24 PM
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.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 10:51:58 PM
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.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 30, 2011, 10:57:34 PM
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!
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on December 30, 2011, 11:30:59 PM
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
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 02:00:04 AM
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.     
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 02:10:14 AM
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?
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on December 31, 2011, 08:35:24 AM
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.
Title: Re: _IMsoDispObj
Post by: José Roca on December 31, 2011, 09:14:32 AM
There are methods that return a pointer to a CoClass instead of an interface. When you try to use this pointer... boom!
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on December 31, 2011, 09:38:37 AM
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.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 04:45:26 PM
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.
Title: Re: _IMsoDispObj
Post by: James C. Fuller on December 31, 2011, 05:32:37 PM
Fred,
  In my opinion I don't think the effort is worth it for obvious MS deviations from  it's own rules.
José has said for years that Excel is a complete mess.
I don't really see a c++ target user for include files for these type of com servers??
What I want are c++ header files for com servers I create with PowerBASIC for use with MinGW c++.

James
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 05:35:26 PM
Another example of this is something I mentioned last week - DataObject, which is a CoClass in MSFlexGridLib....


[uuid(2334D2B2-713E-11CF-8AE5-00AA00C00905), noncreatable] coclass DataObject
{
[default] interface IVBDataObject;
};


Here is the dispinterface from OLEView.  Note the last two OLEGragDrop methods return a pointer to a CoClass!


[uuid(609602E0-531B-11CF-91F6-C2863C385E30), helpstring("Event interface for Microsoft FlexGrid Control"), helpcontext(0x00059620), hidden] dispinterface DMSFlexGridEvents
{
properties:
methods:
[id(0xfffffda8), helpstring("Fired when the user presses and releases the mouse button over the control."), helpcontext(0x000591d1)] void Click();
[id(0xfffffda6), helpstring("Fired when the user pushes a key."), helpcontext(0x000591d2)] void KeyDown(short* KeyCode, short Shift);
[id(0xfffffda7), helpstring("Fired when the user double-clicks the mouse over the control."), helpcontext(0x000591d3)] void DblClick();
[id(0xfffffda5), helpstring("Fired when the user presses a key."), helpcontext(0x000591d4)] void KeyPress(short* KeyAscii);
[id(0xfffffda4), helpstring("Fired when the user releases a key."), helpcontext(0x000591d5)] void KeyUp(short* KeyCode, short Shift);
[id(0xfffffda3), helpstring("Fired when the user presses a mouse button over the control."), helpcontext(0x000591d6)] void MouseDown(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
[id(0xfffffda2), helpstring("Fired when the user moves the mouse over the control."), helpcontext(0x000591d7)] void MouseMove(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
[id(0xfffffda1), helpstring("Fired when the user releases a mouse button over the control."), helpcontext(0x000591d8)] void MouseUp(short Button, short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
[id(0x00000045), helpstring("Fired when the selected range of cells changes."), helpcontext(0x0005920e)] void SelChange();
[id(0x00000046), helpstring("Fired when the current cell changes."), helpcontext(0x0005920a)] void RowColChange();
[id(0x00000047), helpstring("Fired before the cursor enters a cell."), helpcontext(0x000591f7)] void EnterCell();
[id(0x00000048), helpstring("Fired after the cursor leaves a cell."), helpcontext(0x00059201)] void LeaveCell();
[id(0x00000049), helpstring("Fired when the TopRow or LeftCol properties change."), helpcontext(0x0005920d)] void Scroll();
[id(0x0000004a), helpstring("Fired during custom sorts to compare two rows."), helpcontext(0x000591f6)] void Compare(long Row1, long Row2, short* Cmp);
[id(0x0000060e), helpstring("OLEStartDrag event"), helpcontext(0x00057e8b)] void OLEStartDrag([in, out] DataObject** Data, [in, out] long* AllowedEffects);
[id(0x0000060f), helpstring("OLEGiveFeedback event"), helpcontext(0x00057e8c)] void OLEGiveFeedback([in, out] long* Effect, [in, out] VARIANT_BOOL* DefaultCursors);
[id(0x00000610), helpstring("OLESetData event"), helpcontext(0x00057e8d)] void OLESetData([in, out] DataObject** Data, [in, out] short* DataFormat);
[id(0x00000611), helpstring("OLECompleteDrag event"), helpcontext(0x00057e8e)] void OLECompleteDrag([in, out] long* Effect);
[id(0x00000612), helpstring("OLEDragOver event"), helpcontext(0x00057e8f)] void OLEDragOver([in, out] DataObject** Data, [in, out] long* Effect, [in, out] short* Button, [in, out] short* Shift, [in, out] single* x, [in, out] single* y, [in, out] short* State);
[id(0x00000613), helpstring("OLEDragDrop event"), helpcontext(0x00057e90)] void OLEDragDrop([in, out] DataObject** Data, [in, out] long* Effect, [in, out] short* Button, [in, out] short* Shift, [in, out] single* x, [in, out] single* y);
};


See the DataObject** just above.  I finally did more TypeLib magic on it so this is auto-generated by my code (same as Jose's and PB Com Browser)...


interface DMSFlexGridEvents : IDispatch
{
void __stdcall Click(void);
void __stdcall KeyDown(signed short* KeyCode, signed short Shift);
void __stdcall DblClick(void);
void __stdcall KeyPress(signed short* KeyAscii);
void __stdcall KeyUp(signed short* KeyCode, signed short Shift);
void __stdcall MouseDown(signed short Button, signed short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void __stdcall MouseMove(signed short Button, signed short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void __stdcall MouseUp(signed short Button, signed short Shift, OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void __stdcall SelChange(void);
void __stdcall RowColChange(void);
void __stdcall EnterCell(void);
void __stdcall LeaveCell(void);
void __stdcall Scroll(void);
void __stdcall Compare(LONG Row1, LONG Row2, signed short* Cmp);
void __stdcall OLEStartDrag(IDispatch** Data, LONG* AllowedEffects);
void __stdcall OLEGiveFeedback(LONG* Effect, VARIANT_BOOL* DefaultCursors);
void __stdcall OLESetData(IDispatch** Data, signed short* DataFormat);
void __stdcall OLECompleteDrag(LONG* Effect);
void __stdcall OLEDragOver(IDispatch** Data, LONG* Effect, signed short* Button, signed short* Shift, float* x, float* y, signed short* State);
void __stdcall OLEDragDrop(IDispatch** Data, LONG* Effect, signed short* Button, signed short* Shift, float* x, float* y);
};


Since the DataObject CoClass only has one interface, and I expect the address of the VTable pointer to IVBDataObject would be identical to the address of the CoClass DataObject, I'm thinking a more COM compliant parameter type would be IVBDataObject instead of DataObject. 
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 05:44:15 PM
Quote
  In my opinion I don't think the effort is worth it for obvious MS deviations from  it's own rules.
José has said for years that Excel is a complete mess.
I don't really see a c++ target user for include files for these type of com servers??
What I want are c++ header files for com servers I create with PowerBASIC for use with MinGW c++.

Your point is well taken James.  Those complexities we are discussing here would delay my presentation of what I have developed here so far quite a bit - at least several weeks I'm thinking.  I do believe what I have so far would satisfactorily auto-generate C++ headers for the vast majority of typelibs.  And believe it or not, my console program (MinGW but no GUI yet) is only 800 lines of code and compiles to 39k.  Like I mentioned to Jose, about 99.99% of the code generated is reasonably easy to extract out of the typelib.   

That business above with searching the registry out trying to come up with missing dependencies really blew my mind!
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on December 31, 2011, 05:54:52 PM
Quote
What I want are c++ header files for com servers I create with PowerBASIC for use with MinGW c++.

Well, at least I have that.  I'll post it shortly.
Title: Re: _IMsoDispObj
Post by: José Roca on December 31, 2011, 06:21:04 PM
Take a look at vbinterf.inc in my headers. The DataObject class it's not creatable: it is implemented in the Visual Basic runtime. It's often used in OCXs made for Visual Basic. Just use IDispatch to allow compilation.
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on December 31, 2011, 07:18:17 PM
It is not uncommon to see parameter and return types named after the CoClass
when the type is the default interface for the CoClass.
I use the same approach when generating the headers.  If the object is present in the
type library, it is in this case, I see no reason to rename DataObject to IDispatch. 
 
This does not require a registry search.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 03, 2012, 09:49:25 PM
I'm still thinking about this issue of one typelib referencing objects in other typelibs.  When Dominic first mentioned his technique of generating or loading several other typelibs in the Excel case, my first thought was, "Wow!, Good idea!"  But then I right away thought, "How did Dominic know which libs to load?"  That is why I made the comment about Jose's method not requiring a priori knowledge about any particular type lib.  Using Jose's technique one has a missing interface name, so one simply scours the registry searching out the typelib it is found in, then loads that typelib too to get the missing interface definition.  But I eventually realized referenced typelibs are listed right at the top of the idl file.  Here is the very top few lines of the Excel typelib (from OLEVIEW dump), showing importlib statements listing the very typelibs Dominic mentioned...


// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: EXCEL9.OLB

[uuid(00020813-0000-0000-C000-000000000046),  version(1.3),  helpstring("Microsoft Excel 9.0 Object Library"),  helpfile("VBAXL9.CHM"),  helpcontext(0x0000ffff)]
library Excel
{
    // TLib :     // TLib : Microsoft Visual Basic for Applications Extensibility 5.3 : {0002E157-0000-0000-C000-000000000046}
    importlib("VBE6EXT.OLB");
    // TLib : Microsoft Office 9.0 Object Library : {2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}
    importlib("MSO9.DLL");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface Adjustments;
    interface CalloutFormat;


But my question is, "How is that information retrieved from the typelib?"  I don't see anything obvious in the ITypeLib or ITypeInfo interfaces.  It seems to me it would be the sort of information one would retrieve through ITypeLib, being as it is info pertaining to the whole library, rather than to any interface.  But nothing obvious is jumping out at me.
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on January 05, 2012, 12:21:15 AM
Quote
But my question is, "How is that information retrieved from the typelib?"  I don't see anything obvious
in the ITypeLib or ITypeInfo interfaces.  It seems to me it would be the sort of information one would
retrieve through ITypeLib, being as it is info pertaining to the whole library, rather than to any interface.
But nothing obvious is jumping out at me.

If you understand the format of a type library file, you can try getting the imports from the file directly.
Tony Burcham did work in that area.

http://theircorp.byethost11.com/index.php?vw=TypeLib

But I would not go that route because the information is provided by the ITypeLib and ITypeInfo interfaces.

Given a TYPEDESC, I first determine whether the vartye resolves to VT_UNKNOWN or VT_DISPATCH. If it does,
that means it has a guid associated with it.
By the way, if the vartype is VT_USERDEFINED, then you will have to do some drilling. Working with a TYPEDESC
requires a recursive function. That function will also have to deal with the cases when the TYPEATTR returns
TKIND_ALIAS and TKIND_COCLASS.

Examples of TKIND_ALIAS are IFontDisp and IPictureDisp.

Once I have a guid I test whether the interface is internal or external to the current type library.


FUNCTION IsExternalInterface _
  ( _
  BYVAL pITypeLib AS DWORD, _
        rguid     AS GUID _
  ) AS LONG

  LOCAL pITypeInfo  AS DWORD
  LOCAL hr          AS LONG
  LOCAL lRet        AS LONG

  lRet = %TRUE

  hr = ITypeLib_GetTypeInfoOfGuid(pITypeLib, rguid, pITypeInfo)
  IF hr = %S_OK THEN
    lRet = %FALSE
    ITypeInfo_Release pITypeInfo
  END IF

  FUNCTION = lRet

END FUNCTION
 

If it is external you can do a registry search or let the OS do the walking.
The GetTypeLibFromInterfaceIID function below shows one way to do the registry search.

You can let the OS do the search by using this code


      ' This is an interface - find the type library it belongs to
      ' Note: ITypeInfo::GetContainingTypeLib finds the library the interface belongs to even
      '       if it is not in the current type library.
      hr = ITypeInfo_GetContainingTypeLib(pITypeInfo, pITypeLib, dwIndex)
      IF hr = %S_OK THEN
        hr = ITypeLib_GetLibAttr(pITypeLib, pTLibAttr)
        IF hr = %S_OK THEN
          liid      = @pTLibAttr.rguid
          wMajorVer = @pTLibAttr.wMajorVerNum
          wMinorVer = @pTLibAttr.wMinorVerNum
          dwLCID    = @pTLibAttr.lcid
          ITypeLib_ReleaseTLibAttr pITypeLib, pTLibAttr
        END IF
        ITypeLib_Release pITypeLib
      END IF


The following code manually searches the registry.


'-------------------------------------------------------------------------------
'
' PROCEDURE: GetTypeLibFromIntetrfaceIID
' PURPOSE:   Returns the GUID of the type library that implements a given
'            interface.
' NOTES:     The version and LCID registry keys are in hexadecimal format.
' RETURN:    S_OK if successful, E_FAIL otherwise.
'
'-------------------------------------------------------------------------------

FUNCTION GetTypeLibFromIntetrfaceIID _
  ( _
  szIID     AS ASCIIZ, _  ' [in] interface ID
  szLIBID   AS ASCIIZ, _  ' [out] type library ID
  wMajorVer AS WORD, _    ' [out] major version
  wMinorVer AS WORD, _    ' [out] minor version
  dwLCID    AS DWORD _    ' [out] locale ID
  ) AS LONG

  LOCAL szKey       AS ASCIIZ * %MAX_PATH
  LOCAL szVersion   AS ASCIIZ * %MAX_PATH
  LOCAL szUUID      AS ASCIIZ * 64
  LOCAL tft         AS FILETIME
  LOCAL hKey        AS DWORD
  LOCAL dwIndex     AS DWORD
  LOCAL dwType      AS DWORD
  LOCAL cb          AS DWORD
  LOCAL lPos        AS LONG
  LOCAL hr          AS LONG

  hr = %E_FAIL

  wMajorVer = 0
  wMinorVer = 0

  ' Searches the HKEY_CLASSES_ROOT\Interface\<IID>\Typelib node.
  szKey = "Interface\"
  lstrcat szKey, szIID
  lstrcat szKey, "\TypeLib"
  IF RegOpenKeyEx(%HKEY_CLASSES_ROOT, szKey, 0, %KEY_QUERY_VALUE OR %KEY_ENUMERATE_SUB_KEYS, hKey) = %ERROR_SUCCESS THEN
    ' Retrrieves the Guid of the TypeLib
    szKey  = ""
    cb = SIZEOF(szUUID)
    IF RegQueryValueEx(hKey, szKey, 0, BYVAL VARPTR(dwType), BYVAL VARPTR(szUUID), BYVAL VARPTR(cb)) = %ERROR_SUCCESS THEN
      szLIBID = szUUID
      hr = %S_OK
      dwIndex = 0

      DO
        szKey = ""
        cb = SIZEOF(szKey)
        IF RegEnumValue(hKey, dwIndex, BYVAL VARPTR(szKey), BYVAL VARPTR(cb), BYVAL %NULL, BYVAL VARPTR(dwType), BYVAL VARPTR(szVersion), %MAX_PATH) <> %ERROR_SUCCESS THEN EXIT DO
        IF UCASE$(szKey) = "VERSION" THEN
          lPos = INSTR(szVersion, ".")
          IF ISTRUE lPos THEN
            wMajorVer = VAL("&h" + MID$(szVersion, 1, lPos - 1))
            wMinorVer = VAL("&h" + MID$(szVersion, lPos + 1))
          ELSE
            wMajorVer = VAL("&h" + szVersion)
            wMinorVer = 0
          END IF
          EXIT DO
        END IF
        INCR dwIndex
      LOOP

    END IF
    RegCloseKey hKey
  END IF

  IF hr = %S_OK THEN
    szKey = "TypeLib\"
    lstrcat szKey, szLIBID
    lstrcat szKey, "\"
    lstrcat szKey, szVersion
    IF RegOpenKeyEx(%HKEY_CLASSES_ROOT, szKey, 0, %KEY_QUERY_VALUE OR %KEY_ENUMERATE_SUB_KEYS, hKey) = %ERROR_SUCCESS THEN
      ' Enumerate locale ID, HELPDIR, and FLAGS
      ' and save the locale ID.
      dwIndex = 0

      DO
        cb = SIZEOF(szKey)
        IF RegEnumKeyEx(hKey, dwIndex, szKey, cb, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL, tft) <> %ERROR_SUCCESS THEN EXIT DO

        SELECT CASE UCASE$(szKey)
          CASE "FLAGS", "HELPDIR"
            ' Do nothing

          CASE ELSE
            dwLCID = VAL("&h" + szKey)
            EXIT DO
        END SELECT

        INCR dwIndex
      LOOP

      RegCloseKey hKey
    END IF
  END IF

  FUNCTION = hr

END FUNCTION

Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 05, 2012, 03:02:05 AM
Thank you very much Dominic.  That is exactly the information I'm looking for.  Tomorrow I'll be working on that.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 05, 2012, 03:15:07 AM
It looks like I just gotta did for it a little more.  Darned typelibs just don't dish it up for you on a silver platter! ;D
Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on January 05, 2012, 06:06:35 AM
After some sleep I realized that what I wrote on the vartye is confusing.
There is a group of functions(GetParameterType is one of them) in my type library that return a vartype
when passed a TYPEDESC. Given a TYPEDESC, you are not going to see a vartype of VT_UNKNOWN or VT_DISPATCH.
The vt member of the TYPEDESC would be VT_USERDEFINED, which means that you have to obtain a TYPEATTR to determine the TKIND.

Before I tie myself in knots, here are the two functions I use to figure this stuff out.


'----------------------------------------------------------------------
'
' FUNCTION: GetContainingTypeLib
' PURPOSE:  Generates the code for the creation of secondary forms.
'
'----------------------------------------------------------------------

FUNCTION GetContainingTypeLib _
  ( _
  BYVAL pITypeInfo    AS DWORD, _         ' [in]
        riid          AS GUID, _          ' [in]
        liid          AS GUID, _          ' [out]
        wMajorVer     AS WORD, _          ' [out]
        wMinorVer     AS WORD, _          ' [out]
        dwLCID        AS DWORD _          ' [out]
  ) AS LONG

  LOCAL pTLibAttr       AS TLIBATTR PTR
  LOCAL pITypeLib       AS DWORD
  LOCAL dwIndex         AS DWORD
  LOCAL hr              AS LONG

  SELECT CASE riid
    CASE $IID_IFONTDISP, $IID_IPICTUREDISP, $IID_IFONTEVENTSDISP
      liid      = $IID_STDOLE2
      wMajorVer = 2
      wMinorVer = 0
      dwLCID    = 0

    CASE $IID_IENUMVARIANT, $IID_IFONT, $IID_IPICTURE
      liid      = $IID_STDOLE2
      wMajorVer = 2
      wMinorVer = 0
      dwLCID    = 0

    CASE ELSE
      ' This is an interface - find the type library it belongs to
      ' Note: ITypeInfo::GetContainingTypeLib finds the library the interface belongs to even
      '       if it is not in the current type library.
      hr = ITypeInfo_GetContainingTypeLib(pITypeInfo, pITypeLib, dwIndex)
      IF hr = %S_OK THEN
        hr = ITypeLib_GetLibAttr(pITypeLib, pTLibAttr)
        IF hr = %S_OK THEN
          liid      = @pTLibAttr.rguid
          wMajorVer = @pTLibAttr.wMajorVerNum
          wMinorVer = @pTLibAttr.wMinorVerNum
          dwLCID    = @pTLibAttr.lcid
          ITypeLib_ReleaseTLibAttr pITypeLib, pTLibAttr
        END IF
        ITypeLib_Release pITypeLib
      END IF
  END SELECT

END FUNCTION

'----------------------------------------------------------------------
'
' FUNCTION: GetContainingTypeLibEx
' PURPOSE:  Generates the code for the creation of secondary forms.
'
'----------------------------------------------------------------------

FUNCTION GetContainingTypeLibEx _
  ( _
  BYVAL pITypeInfo    AS DWORD, _         ' [in]
  BYVAL ptdesc        AS TYPEDESC PTR, _  ' [in]
        riid          AS GUID, _          ' [out]
        liid          AS GUID, _          ' [out]
        wMajorVer     AS WORD, _          ' [out]
        wMinorVer     AS WORD, _          ' [out]
        dwLCID        AS DWORD _          ' [out]
  ) AS LONG

  LOCAL ptadesc         AS ARRAYDESC PTR
  LOCAL ptsab           AS SAFEARRAYBOUND PTR
  LOCAL pITypeInfoRef   AS DWORD
  LOCAL pTypeAttr       AS TYPEATTR PTR
  LOCAL pVardesc        AS VARDESC PTR
  LOCAL hr              AS LONG
  LOCAL n               AS WORD

  LOCAL pITypeInfo2     AS DWORD
  LOCAL pITypeInfoRef2  AS DWORD
  LOCAL pTypeAttr2      AS TYPEATTR PTR
  LOCAL reftype2        AS DWORD
  LOCAL ImplTypeFlags   AS DWORD
  LOCAL iImpl           AS DWORD
  LOCAL fBreak          AS LONG

  SELECT CASE @ptdesc.vt AND &H0FFF
    CASE %VT_PTR
      ' ptdesc->lptdesc points to a TYPEDESC that specifies the thing pointed to
      GetContainingTypeLibEx pITypeInfo, @ptdesc.tdd.lptdesc, riid, liid, wMajorVer, wMinorVer, dwLCID

    CASE %VT_CARRAY
      ' ptdesc->lpadesc points to an ARRAYDESC
      ptadesc = @ptdesc.tdd.lpadesc
      GetContainingTypeLibEx pITypeInfo, VARPTR(@ptadesc.tdescElem), riid, liid, wMajorVer, wMinorVer, dwLCID

    CASE %VT_SAFEARRAY
'    hr = ITypeInfo_GetRefTypeInfo(pITypeInfo, @ptdesc.tdd.hreftype, pITypeInfoRef)
'    IF hr = %S_OK THEN
'      ITypeInfo_Release pITypeInfoRef
'    END IF

    CASE %VT_USERDEFINED
      ' Use ptdesc->hreftype and pti->GetRefTypeInfo
      hr = ITypeInfo_GetRefTypeInfo(pITypeInfo, @ptdesc.tdd.hreftype, pITypeInfoRef)
      IF hr = %S_OK THEN
        hr = ITypeInfo_GetTypeAttr(pITypeInfoRef, pTypeAttr)
        IF hr = %S_OK THEN

          SELECT CASE @pTypeAttr.typekind
            CASE %TKIND_ALIAS
              ' IFontDisp and IPictureDisp are aliases for the Font and Picture objects respectively
              GetContainingTypeLibEx pITypeInfoRef, VARPTR(@pTypeAttr.tdescAlias), riid, liid, wMajorVer, wMinorVer, dwLCID

            CASE %TKIND_ENUM
              riid = @pTypeAttr.rguid
              GetContainingTypeLib pITypeInfoRef, riid, liid, wMajorVer, wMinorVer, dwLCID

            CASE %TKIND_DISPATCH
              riid = @pTypeAttr.rguid
              GetContainingTypeLib pITypeInfoRef, riid, liid, wMajorVer, wMinorVer, dwLCID

            CASE %TKIND_INTERFACE
              riid = @pTypeAttr.rguid
              GetContainingTypeLib pITypeInfoRef, riid, liid, wMajorVer, wMinorVer, dwLCID

            CASE %TKIND_COCLASS
              fBreak = 0
              ' Get the default interface
              iImpl = 0

              DO
                IF iImpl >= @pTypeAttr.cImplTypes THEN EXIT DO
        hr = ITypeInfo_GetRefTypeOfImplType(pITypeInfoRef, iImpl, reftype2)
        IF hr = %S_OK THEN
                  hr = ITypeInfo_GetRefTypeInfo(pITypeInfoRef, reftype2, pITypeInfoRef2)
        IF hr = %S_OK THEN
                    hr = ITypeInfo_GetTypeAttr(pITypeInfoRef2, pTypeAttr2)
                    IF hr = %S_OK THEN

                      SELECT CASE @pTypeAttr2.typekind
                        CASE %TKIND_DISPATCH
                          hr = ITypeInfo_GetImplTypeFlags(pITypeInfoRef, iImpl, implTypeFlags)
                    IF hr = %S_OK THEN
                            IF ISTRUE (ImplTypeFlags AND %IMPLTYPEFLAG_FDEFAULT) THEN
                              IF ISFALSE  (ImplTypeFlags AND %IMPLTYPEFLAG_FSOURCE) THEN
                                fBreak = -1
                                riid = @pTypeAttr2.rguid
                                GetContainingTypeLib pITypeInfoRef2, riid, liid, wMajorVer, wMinorVer, dwLCID
                              END IF
                            END IF
                          END IF

                        CASE %TKIND_INTERFACE
                          hr = ITypeInfo_GetImplTypeFlags(pITypeInfoRef, iImpl, implTypeFlags)
                    IF hr = %S_OK THEN
                            IF ISTRUE (ImplTypeFlags AND %IMPLTYPEFLAG_FDEFAULT) THEN
                              IF ISFALSE  (ImplTypeFlags AND %IMPLTYPEFLAG_FSOURCE) THEN
                                fBreak = -1
                                riid = @pTypeAttr2.rguid
                                GetContainingTypeLib pITypeInfoRef2, riid, liid, wMajorVer, wMinorVer, dwLCID
                              END IF
                            END IF
                          END IF
                      END SELECT

                      ITypeInfo_ReleaseTypeAttr pITypeInfoRef2, pTypeAttr2
                    END IF
                    ITypeInfo_Release pITypeInfoRef2
                  END IF
                END IF
                IF fBreak THEN EXIT DO
                INCR iImpl
              LOOP

            CASE %TKIND_UNION
              riid = @pTypeAttr.rguid
              GetContainingTypeLib pITypeInfoRef, riid, liid, wMajorVer, wMinorVer, dwLCID

            CASE %TKIND_RECORD
              riid = @pTypeAttr.rguid
              GetContainingTypeLib pITypeInfoRef, riid, liid, wMajorVer, wMinorVer, dwLCID
          END SELECT

          ITypeInfo_ReleaseTypeAttr pITypeInfoRef, pTypeAttr
        END IF
        ITypeInfo_Release pITypeInfoRef
      END IF
  END SELECT

END FUNCTION

Title: Re: _IMsoDispObj
Post by: Dominic Mitchell on January 05, 2012, 06:39:42 AM
By the way, I use the following structure to keep track of what has to be and what
has been generated.


' Used to determine the type libraries for which
' include metastatements should be generated
TYPE WRITETYPELIBRARYEX ' wtlx
  fImported     AS LONG
  fDone         AS LONG
  fNew          AS LONG                   ' TRUE if a new file was added to the project
  uuid          AS GUID                   ' library ID
  dwLCID        AS DWORD
  wMajorVer     AS WORD
  wMinorVer     AS WORD
  szPrefix      AS ASCIIZ * %CBCTRLNAME
  szCLSID       AS ASCIIZ * 40
  fLoadFromFile AS LONG
  szFile        AS ASCIIZ * %MAX_PATH
  pRefId        AS GUID PTR               ' pointer to array(size cRefId) of uuids for type infos
  pRefDone      AS LONG PTR               ' pointer to array(size cRefId) of flags for type infos
  cRefId        AS LONG                   ' count of type infos in library(ITypeLib::GetTypeInfoCount)
END TYPE


Also, the value of the szPrefix member is set by the code generator. The user has no say in the name
of the prefix used for interfaces.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 05, 2012, 04:25:36 PM
Isn't Tony Burcham the fellow who was/is working on reading biff, i.e., Excel files directly instead of using COM?  I seem to vaguely remember that, as I had been a bit interested in it as I use Excel data quite a bit and Excel is rather slow what with dispinterfaces and all that. 

Anyway, what I am gathering from what you and Jose are telling me is that for virtually every interface parameter type or base class of an interface (_IMsoDispObj was what started all this) I am first going to have to try to determine if its in the present type library being analyzed, for example your IsExternalInterface() function, and if it isn't, call those additional or registry functions to locate the referenced type library?  That seems like the addition of an incredible amount of additional processing overhead for something that should be relatively simple, for example obtaining the couple import libs specified at the top of the idl file.  Just to check this I put a temporary global counter variable in my recursive function which bores through the type descriptions to get the base object type, and for the large MS Excel Type library I ended up with guess how many calls?  36842.  Here is that function...


void PrintVar(ITypeInfo* pTypInfo, TYPEDESC* pTypDesc)
{
switch(pTypDesc->vt)  // For a situation such as Connection**, pTypeDesc won't
{                     // return Connection, but rather VT_PTR, and it'll return
    case VT_PTR:       // this twice; until you call this function enough times
      {                // recursively to burrow through to a 'Connection' UDT.
         TYPEDESC* pTypeDesc=pTypDesc->lptdesc;
         if(pTypeDesc->vt==VT_PTR || pTypeDesc->vt==VT_USERDEFINED)
            PrintVar(pTypInfo, pTypeDesc);       // recursive call
         else
            PrintVarTypeFile(fp,pTypeDesc->vt);  // just output it
         fprintf(fp,"*");
      }
      break;
    case VT_USERDEFINED:
      {
         HRESULT hr;
         ITypeInfo* pRefIntTypInfo=NULL;
         hr=pTypInfo->GetRefTypeInfo(pTypDesc->hreftype,&pRefIntTypInfo);
         if(SUCCEEDED(hr))
         {
            TYPEATTR* pTypeAttr=NULL;
            hr=pRefIntTypInfo->GetTypeAttr(&pTypeAttr);
            if(SUCCEEDED(hr))
            {
               BSTR strName;
               if(pTypeAttr->typekind==TKIND_COCLASS)
               {
                  HREFTYPE pRefType;
                  ITypeInfo* pRefTypeInfo=NULL;
                  hr=pTypInfo->GetRefTypeOfImplType(0,&pRefType);
                  if(SUCCEEDED(hr))
                  {
                     hr=pTypInfo->GetRefTypeInfo(pRefType,&pRefTypeInfo);
                     if(SUCCEEDED(hr))
                     {
                        hr=pRefTypeInfo->GetDocumentation(-1,&strName,NULL,NULL,NULL);
                        fwprintf(fp,L"%s",strName);
                        pRefTypeInfo->Release();
                     }
                  }
               }
               else
               {
                  hr=pRefIntTypInfo->GetDocumentation(MEMBERID_NIL,&strName,NULL,NULL,NULL);
                  if(SUCCEEDED(hr))
                  {
                     fwprintf(fp,L"%s",strName);
                     SysFreeString(strName);
                  }
               }
            }
            pRefIntTypInfo->Release();
         }
      }
      break;
    default:
      PrintVarTypeFile(fp,pTypDesc->vt);
}
}


The idea of having to do all that just for base interfaces, as is the case with _MsoDispObj, is bad enough.  But am I not right that parameter types could also be external objects to the type library?   
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 05, 2012, 05:14:10 PM
Well, I haven't digested it all yet but I seem to be zeroing in on...


ITypeInfo::GetContainingTypeLib

HRESULT GetContainingTypeLib
(
  ITypeLib FAR* FAR*  ppTLib, 
  unsigned int FAR*  pIndex 
);

Retrieves the containing type library and the index of the type description within that type library.

Parameters

ppTLib      On return, points to the containing type library.
pIndex     On return, points to the index of the type description within the containing type library.


...as being the solution here.  That is, if I don't mind searching the typelib I'm presently doing 39587 times to see if the specific object I just got is in this type lib or not! :)  I don't know how I missed that one.  I needed to have my mind bumped in that direction I guess!  Now that's a funny function.  Both parameters are output parameters.
Title: Re: _IMsoDispObj
Post by: José Roca on January 05, 2012, 07:05:22 PM
I only search for external interfaces if the interface that I'm parsing does not inherit directly from IUnknown or IDispatch. For PowerBASIC, I need to parse the external interface because I can't do INHERIT <external interface>, but have to include the methods of that external interface inline.

Also, the function must be recursive because that external interface could inherit from another internal or external interface.
Title: Re: _IMsoDispObj
Post by: Frederick J. Harris on January 05, 2012, 07:32:44 PM
Quote
I only search for external interfaces if the interface that I'm parsing does not inherit directly from IUnknown or IDispatch. For PowerBASIC, I need to parse the external interface because I can't do INHERIT <external interface>, but have to include the methods of that external interface inline.

Also, the function must be recursive because that external interface could inherit from another internal or external interface.

Yes, its slowly becomming clearer to me now.  What you said above would certainly narrow down the amount of searching needing to be done.  You also seem to be saying its not necessary to check interface method parameters to be sure one hasn't come through defined elsewhere?

I believe the question now occurring to me is whether the entirety of the referenced type lib needs to be output, or whether one can just pick and choose from the missing interfaces encountered in the type lib presently being processed?  For example, in the case of index 0 object in the Excel TypeLib which is an IDispatch based interface named Adjustments, its base interface is _IMsoDispObj, as previously described.  And that object is in the Office Dll or type lib, which apparently services in one way or another all or some of the other Office products such as Access, Word, etc.  So its likely there are things in there that have nothing to do with Excel.  If this is the case it doesn't make much sense outputting the entirety of the referenced typelib.  But, what if Interface _IMsoDispObj has a method with a parameter (be it another interface, an enum, or an alias) defined elsewhere in the Office typelib, but not in the Excel typelib?  Then that interface would not compile when its methods were put 'in line' in the Adjustments Interface in the Excel output?  I don't believe that's the case in that specific circumstance, but it appears to me it could happen?