• Welcome to Powerbasic Museum 2020-B.
 

Getting The BSTR Name of IDispatch [out, retval] Parameters

Started by Frederick J. Harris, November 16, 2011, 09:20:34 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

I'm stuck again Jose.  Bad.  I'm hoping you'll help.  I'm still working on typelib code.  I'm trying to obtain the BSTR, that is "vol", representing the last [out, retval] parameter to this bit of IDL....


interface IBFVolume : IDispatch
{
[id(0x00000007)] HRESULT BFVolume([in] short sp, [in] double dbh, [in] double ht, [in] short form_class, [out, retval] double* vol);
};


Note the last parameter is [out, retval] double* vol.  This is actually from my Dispatch example I posted about a year ago.  Here's the approximate output from your TypeLib Browser...


INTERFACE IBFVolume $IID_IBFVolume : INHERIT IDispatch
  METHOD BFVolume <7> _
  ( _                                                    ' VTable offset = 28
      BYVAL INTEGER _                                    ' [in] sp VT_I2 <Integer>
    , BYVAL DOUBLE _                                     ' [in] dbh VT_R8 <Double>
    , BYVAL DOUBLE _                                     ' [in] ht VT_R8 <Double>
    , BYVAL INTEGER _                                    ' [in] form_class VT_I2 <Integer>
  ) AS DOUBLE                                            ' [retval][out] *vol VT_R8 <Double>
END INTERFACE


And here it is from the PowerBASIC COM Browser configured for ID Binding...


Interface IDBind IBFVolume
  Member Call BFVolume <7> (In sp As Integer<0>, In dbh As Double<1>, In ht As Double<2>, In form_class As Integer<3>) As Double
End Interface


Of course, in both your Type Lib Browser, and the PowerBASIC one the method is typed as returning a double and only has four parameters instead of the five in the IDL and C++ syntax.  I understand that the [out, retval] parameter has been transposed to the method return value.  However, in your output from your TypeLib Browser I don't know how you are coming up with the "vol" parameter name.  That IS my question.  How in the world do you get that?

There are some differences in the output between yours and the PowerBASIC one.  I see that the PowerBASIC COM Browser is adding zero based <dispids> to the parameters, and yours doesn't.     

I wouldn't ask if there was any amount of time I could expend to figure this out, but I've already been trying for days with no success whatsoever.  I've tried countless things.  Below is my best attempt, followed by the output....


#define _WIN32_DCOM
#include <windows.h>
#include <cstdio>
#include <oaidl.h>
#include "TlbTypes.h"
#define  MAX_NAMES  16
const IID LIBID_Component = {0x20000006,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}}; // ForUtils 1,1, ...

int main()
{
BSTR rgBStrNames[MAX_NAMES];
UINT cMaxNames = MAX_NAMES;
ITypeInfo* pTypeInfo=NULL;
TYPEATTR* pTypeAttr=NULL;
FUNCDESC* pFuncDesc=NULL;
ITypeLib* pTypeLib=NULL;
UINT pcNames=NULL;
TYPEKIND pKind;
BSTR strName;
HRESULT hr;
int i,h=0;

CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr=LoadRegTypeLib(LIBID_Component,1,1,0,&pTypeLib);
if(SUCCEEDED(hr))
{
    hr=pTypeLib->GetTypeInfoType(h,&pKind);       // h=0 is index of IBFVolume Dispatch interface
    if(SUCCEEDED(hr))
    {
       PrintTypeKind(h,pKind);                                   
       pTypeLib->GetDocumentation(h,&strName,NULL,NULL,NULL);    // Get doc string for index = 0
       wprintf(L"\nstrName(%d)                                = %s\n",h,strName);
       SysFreeString(strName);
       hr=pTypeLib->GetTypeInfo(h,&pTypeInfo);                   // Get pTypeInfo for IBFVolume interface
       if(SUCCEEDED(hr))
       {
          hr=pTypeInfo->GetTypeAttr(&pTypeAttr);                 // Get Type Attribute pointer for IBFVolume Interface
          if(SUCCEEDED(hr))
          {                                                      // Get # of Fns in IBFVolume.  3 for IUNknown; 4 for IDispatch; and
             printf("pTypeAttr->cFuncs                         = %u\n",pTypeAttr->cFuncs);   // 1 for IBFVolume = 8 total
             hr=pTypeInfo->GetFuncDesc(7,&pFuncDesc);            // The '8' is zero based, so I assigned '7' to BFVolume in IDL file
             if(SUCCEEDED(hr))                                   // as the <dispid>
             {
                PrintFuncKind(0,pFuncDesc->funckind);
                printf("\nCalling Convention                        = ");
                PrintCallingConvention(pFuncDesc->callconv);
                for(i=0; i<MAX_NAMES; i++)                       // Allocate a few zero length BSTRs
                    rgBStrNames[i]=SysAllocString(L"");
                printf(  "pFuncDesc->memid                          = %u\n",pFuncDesc->memid);     // Output memid
                printf(  "pFuncDesc->cParams                        = %u\n",pFuncDesc->cParams);   // Output # params
                printf(  "pFuncDesc->elemdescFunc.tdesc.vt          = %u\n",pFuncDesc->elemdescFunc.tdesc.vt); // Output type of retval
                hr=pTypeInfo->GetNames(pFuncDesc->memid,rgBStrNames,cMaxNames,&pcNames);                       //      (double)
                if(SUCCEEDED(hr))
                {
                   printf("cMaxNames                                 = %d\n",cMaxNames);
                   printf("pcNames                                   = %d\n",pcNames);
                   wprintf(L"rgBStrNames[0]                            = %s\n",rgBStrNames[0]);
                   wprintf(L"rgBStrNames[1]                            = %s\n",rgBStrNames[1]);
                   wprintf(L"rgBStrNames[2]                            = %s\n",rgBStrNames[2]);
                   wprintf(L"rgBStrNames[3]                            = %s\n",rgBStrNames[3]);
                   wprintf(L"rgBStrNames[4]                            = %s\n",rgBStrNames[4]);
                   printf("pFuncDesc->lprgelemdescParam[0].tdesc.vt  = %u\n",pFuncDesc->lprgelemdescParam[0].tdesc.vt);
                   printf("pFuncDesc->lprgelemdescParam[1].tdesc.vt  = %u\n",pFuncDesc->lprgelemdescParam[1].tdesc.vt);
                   printf("pFuncDesc->lprgelemdescParam[2].tdesc.vt  = %u\n",pFuncDesc->lprgelemdescParam[2].tdesc.vt);
                   printf("pFuncDesc->lprgelemdescParam[3].tdesc.vt  = %u\n\n",pFuncDesc->lprgelemdescParam[3].tdesc.vt);
                }
                for(i=0; i<MAX_NAMES; i++)
                    SysFreeString(rgBStrNames[i]);
                pTypeInfo->ReleaseFuncDesc(pFuncDesc);
             }
             pTypeInfo->ReleaseTypeAttr(pTypeAttr);
          }
          pTypeInfo->Release();
       }
    }
    pTypeLib->Release();
}
CoUninitialize();
getchar();

return 0;
}

/*
Output:

pKind(0)                                  = TKIND_DISPATCH
strName(0)                                = IBFVolume
pTypeAttr->cFuncs                         = 8
pKind(0)                                  = FUNC_DISPATCH
Calling Convention                        = __stdcall
pFuncDesc->memid                          = 7
pFuncDesc->cParams                        = 4
pFuncDesc->elemdescFunc.tdesc.vt          = 5
cMaxNames                                 = 16
pcNames                                   = 5
rgBStrNames[0]                            = BFVolume
rgBStrNames[1]                            = sp
rgBStrNames[2]                            = dbh
rgBStrNames[3]                            = ht
rgBStrNames[4]                            = form_class
pFuncDesc->lprgelemdescParam[0].tdesc.vt  = 2
pFuncDesc->lprgelemdescParam[1].tdesc.vt  = 5
pFuncDesc->lprgelemdescParam[2].tdesc.vt  = 5
pFuncDesc->lprgelemdescParam[3].tdesc.vt  = 2
*/




About in the middle of the code you can see where I'm getting a FUNCDESC pointer for the index = 7 method in the IBFVolume interface as described above.  Of course, I assigned the <dispid> of '7' in my idl file.  After doing that I retrieve the count of parameters in the method and the return I get, i.e., pFuncDesc->cParams = 4.  So its obviously not counting the 5th [out, retval] parameter as a parameter. True to that line of logic, when I then call...


pTypeInfo->GetNames(pFuncDesc->memid, rgBStrNames, cMaxNames, &pcNames);


...it is telling me it came up with 5 names, counting the name of the method BFVolume in the zeroth array element as so...


pcNames                = 5
rgBStrNames[0]     = BFVolume
rgBStrNames[1]     = sp
rgBStrNames[2]     = dbh
rgBStrNames[3]     = ht
rgBStrNames[4]     = form_class


But as you can see, there is no 5th double* vol parameter.  So that's what is happening in this case, but I can find no documentation to tell me how to get the names of those parameters GetNames() won't return.  However, you clearly managed it. 

I poured through that link you gave me by Sean Baxter.  Its kind of amazing I got as far as I did without that.  It was a good link and I printed out the whole thing and studied it but was unable to find an answer there unless I missed it somehow.

For what I'm doing I really need and want to have that information.

The member of ITypeInfo that I'm most disappointed in in terms of getting the information I want is ITypeInfo::GetNames...


ITypeInfo::GetNames

HRESULT GetNames
(
MEMBERID            memid,         
BSTR FAR*           rgBstrNames, 
unsigned int        cMaxNames, 
unsigned int FAR*   pcNames 
);

Retrieves the variable with the specified member ID (or the name of the property or method and its parameters) that correspond
to the specified function ID.


Their statement 'Retrieves the variable with the specified member ID...'  seems to indicate to me that if I pass in a <dispid> of a variable, such as the sequentially numbered dispids shown in the PowerBASIC declare first shown, I'd be able to retrieve from the BSTR the parameter name I want.  But I've tried that and can't get it to work.  I tried passing in both 4 and 5 (as well as a lot of other numbers).  So my question is, 'How does one obtain the parameter name string for an [out, retval] such as my 'vol' above?


José Roca

Dual interfaces have two views, the VTable view (aka C view) and the Automation view (aka VB view). The default is the VB view, since all that automation mess was developed by the VB team.

When using the VTable view, the return type is almost always HRESULT, and the out retval parameter is returned, including its name, as the last one by GetNames. When using the Automation view, the out retval parameter is returned as the return type, not as the last parameter, and the HRESULT is returned by other means, e.g. OBJRESULT in PowerBASIC.

To change the default automation view to VTable view, you need to call ITypeInfo::GetRefTypeOfImplType passing an index of –1.

It is explained in http://spec.winprog.org/typeinf3/

Quote
If you were really paying attention in Part II, you'd already have identified a problem with CComInterfaceGroupContainer::expand.  So let me clue you in: dual interfaces are always exposed as type TKIND_DISPATCH.  To quote the Automation Programmer's Reference, "For dual interfaces, ITypeLib::GetTypeInfo returns only the TKIND_DISPATCH type information.  To get the TKIND_INTERFACE type information, ITypeInfo::GetRefTypeOfImplType can be called on the TKIND_DISPATCH type information, passing an index of –1.   Then, the returned type information handle can be passed to ITypeInfo::GetRefTypeInfo."  This is easily taken care of with an expand specialization:


template<> void CComInterfaceGroupContainer<TKIND_INTERFACE>::expand() {
    KillExpansionNode();
    if(!_pTypeLib) return;
    UINT infoCount(_pTypeLib->GetTypeInfoCount());
    if(!infoCount) return;
    for(int info(0); info < infoCount; ++info) {
        try {
            TYPEKIND typeKind;
            HRESULT hr(_pTypeLib->GetTypeInfoType(info, &typeKind));
            if(hr) continue;
            if(typeKind == TKIND_DISPATCH) {
                CComPtr<ITypeInfo> pDispatchInfo;
                hr = _pTypeLib->GetTypeInfo(info, &pDispatchInfo);
                if(hr) continue;
                HREFTYPE interfaceRefType;
                hr = pDispatchInfo->GetRefTypeOfImplType(-1, &interfaceRefType);
                if(hr) continue;
                CComPtr<ITypeInfo> pInterfaceInfo;
                hr = pDispatchInfo->GetRefTypeInfo(interfaceRefType,
                    &pInterfaceInfo);
                if(hr) continue;
                CComTypeAttr typeAttr(pInterfaceInfo);
                if(typeAttr->typekind != TKIND_INTERFACE) continue;
                CComBSTR name;
                hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
                if(hr) continue;
                _interfaces.push_back(CComInterfaceJoint(name, treeView(),
                    treeItem(), pInterfaceInfo));
                continue;
            }
            if(typeKind != TKIND_INTERFACE) continue;
            CComBSTR name;
            hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
            if(hr) continue;
            CComPtr<ITypeInfo> pTypeInfo;
            hr = _pTypeLib->GetTypeInfo(info, &pTypeInfo);
            if(hr) continue;
            _interfaces.push_back(CComInterfaceJoint(name, treeView(),
                treeItem(), pTypeInfo));
        } catch(EVeryBadThing) { }
    }
}


José Roca

Quote
There are some differences in the output between yours and the PowerBASIC one.  I see that the PowerBASIC COM Browser is adding zero based <dispids> to the parameters, and yours doesn't.   

The DispIDs are only needed when using IDBIND interface declarations with PB's Automation statements: OBJECT CALL/GET/SET/LET. My browser has an option to generate IDBIND type interfaces as the last one of the "Code" toolbar dropdown button (shortcut: F7).

Frederick J. Harris

Thanks very, very much Jose.  Now I've got it.  Somehow I missed that in Sean Baxter's document.  I'd describe that as a rather big gotcha!  The reason I mentioned dispids of parameters is that I kept thinking there must be some function somewhere that if one could determine the dispid of the missing parameter, you could pass it into a function/method and get the data for it.  The 'view' thing was totally lost to me.  Anyway, here's my code and output now....


#define _WIN32_DCOM
#include <windows.h>
#include <cstdio>
#include <oaidl.h>
#define  MAX_NAMES  16
const IID LIBID_Component = {0x20000006,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}};  // ForUtils 1, 1, ...

int main()
{
ITypeInfo*   pInterfaceTypeInfo=NULL;
ITypeInfo*   pDispatchTypeInfo=NULL;
BSTR         rgBStrNames[MAX_NAMES];
UINT         cMaxNames = MAX_NAMES;
HREFTYPE     InterfaceRefType;
TYPEATTR*    pTypeAttr=NULL;
FUNCDESC*    pFuncDesc=NULL;
ITypeLib*    pTypeLib=NULL;
UINT         pcNames=NULL;
TYPEKIND     pKind;
HRESULT      hr;
int          i,h=0;

CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr=LoadRegTypeLib(LIBID_Component,1,1,0,&pTypeLib);
if(SUCCEEDED(hr))
{
    hr=pTypeLib->GetTypeInfoType(h,&pKind);
    if(SUCCEEDED(hr))
    {
       if(pKind==TKIND_DISPATCH)
       {
          printf("pKind                                     = TKIND_DISPATCH\n");
          hr = pTypeLib->GetTypeInfo(h, &pDispatchTypeInfo); 
          if(SUCCEEDED(hr))
          {
             hr = pDispatchTypeInfo->GetRefTypeOfImplType(-1, &InterfaceRefType);
             if(SUCCEEDED(hr))
             {
                hr = pDispatchTypeInfo->GetRefTypeInfo(InterfaceRefType,&pInterfaceTypeInfo);
                if(SUCCEEDED(hr))
                {
                   hr=pInterfaceTypeInfo->GetTypeAttr(&pTypeAttr);
                   if(SUCCEEDED(hr))
                   {
                      if(pTypeAttr->typekind==TKIND_INTERFACE)
                      {
                         printf("pKind                                     = TKIND_INTERFACE\n");
                         printf("pTypeAttr->cFuncs                         = %u\n",pTypeAttr->cFuncs);
                         pInterfaceTypeInfo->GetFuncDesc(0,&pFuncDesc);
                         if(SUCCEEDED(hr))
                         {
                            if(pFuncDesc->invkind==INVOKE_FUNC)
                            {
                               if(pFuncDesc->callconv==CC_STDCALL)
                                  printf("Calling Convention                        = __stdcall\n");
                               for(i=0; i<MAX_NAMES; i++)
                                   rgBStrNames[i]=SysAllocString(L"");
                               printf(  "pFuncDesc->memid                          = %u\n",pFuncDesc->memid);
                               printf(  "pFuncDesc->cParams                        = %u\n",pFuncDesc->cParams);
                               printf(  "pFuncDesc->elemdescFunc.tdesc.vt          = %u (25 = HRESULT)\n",pFuncDesc->elemdescFunc.tdesc.vt);
                               hr=pInterfaceTypeInfo->GetNames(pFuncDesc->memid,rgBStrNames,cMaxNames,&pcNames);
                               if(SUCCEEDED(hr))
                               {
                                  printf("cMaxNames                                 = %d\n",cMaxNames);
                                  printf("pcNames                                   = %d\n",pcNames);
                                  wprintf(L"rgBStrNames[0]                            = %s\n",rgBStrNames[0]);
                                  wprintf(L"rgBStrNames[1]                            = %s\n",rgBStrNames[1]);
                                  wprintf(L"rgBStrNames[2]                            = %s\n",rgBStrNames[2]);
                                  wprintf(L"rgBStrNames[3]                            = %s\n",rgBStrNames[3]);
                                  wprintf(L"rgBStrNames[4]                            = %s\n",rgBStrNames[4]);
                                  wprintf(L"rgBStrNames[5]                            = %s\n",rgBStrNames[5]);
                                  printf("pFuncDesc->lprgelemdescParam[0].tdesc.vt  = %u  ( 2 = VT_I2)\n",pFuncDesc->lprgelemdescParam[0].tdesc.vt);
                                  printf("pFuncDesc->lprgelemdescParam[1].tdesc.vt  = %u  ( 5 = VT_R8)\n",pFuncDesc->lprgelemdescParam[1].tdesc.vt);
                                  printf("pFuncDesc->lprgelemdescParam[2].tdesc.vt  = %u  ( 5 = VT_R8)\n",pFuncDesc->lprgelemdescParam[2].tdesc.vt);
                                  printf("pFuncDesc->lprgelemdescParam[3].tdesc.vt  = %u  ( 2 = VT_I2)\n",pFuncDesc->lprgelemdescParam[3].tdesc.vt);
                                  printf("pFuncDesc->lprgelemdescParam[4].tdesc.vt  = %u (26 = VT_PTR)\n",pFuncDesc->lprgelemdescParam[4].tdesc.vt);
                               }
                               for(i=0; i<MAX_NAMES; i++)
                                   SysFreeString(rgBStrNames[i]);
                            }
                            pInterfaceTypeInfo->ReleaseFuncDesc(pFuncDesc);
                         }
                      }
                      pInterfaceTypeInfo->ReleaseTypeAttr(pTypeAttr);
                   }
                   pInterfaceTypeInfo->Release();
                }
             }
             pDispatchTypeInfo->Release();
          }
       }
    }
    pTypeLib->Release();
}
CoUninitialize();
getchar();

return 0;
}

/*
Output:
============================================================
pKind                                     = TKIND_DISPATCH
pKind                                     = TKIND_INTERFACE
pTypeAttr->cFuncs                         = 1
Calling Convention                        = __stdcall
pFuncDesc->memid                          = 7
pFuncDesc->cParams                        = 5
pFuncDesc->elemdescFunc.tdesc.vt          = 25 (25 = HRESULT)
cMaxNames                                 = 16
pcNames                                   = 6
rgBStrNames[0]                            = BFVolume
rgBStrNames[1]                            = sp
rgBStrNames[2]                            = dbh
rgBStrNames[3]                            = ht
rgBStrNames[4]                            = form_class
rgBStrNames[5]                            = vol
pFuncDesc->lprgelemdescParam[0].tdesc.vt  = 2  ( 2 = VT_I2)
pFuncDesc->lprgelemdescParam[1].tdesc.vt  = 5  ( 5 = VT_R8)
pFuncDesc->lprgelemdescParam[2].tdesc.vt  = 5  ( 5 = VT_R8)
pFuncDesc->lprgelemdescParam[3].tdesc.vt  = 2  ( 2 = VT_I2)
pFuncDesc->lprgelemdescParam[4].tdesc.vt  = 26 (26 = VT_PTR)
*/