Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get members of COM object via Delphi Olevariant type

Tags:

com

delphi

Is it possible to get the list of members (properties, functions, procedures) for a COM object via the OleVariant type?

For example,

var
  wscript: Olevariant;
begin
  wscript := CreateOleObject("WScript.Shell");
  ...
end;

and I am particularly interested in getting a list of functions such as WScript.Echo, WScript.Quit etc.

I want to have this feature because it is good to implement the code auto completion.

like image 254
justyy Avatar asked Oct 30 '13 12:10

justyy


2 Answers

You can use the GetTypeInfo method and the ITypeInfo interface.

Try this sample code (is not complete but you can use it as starting point)

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

//http://spec.winprog.org/typeinfo/
//http://spec.winprog.org/typeinf2/
//http://spec.winprog.org/typeinf3/

function GetTypeStr(tdesc : TTypeDesc; Context : ActiveX.ITypeinfo):string;
var
  tinfo    : ActiveX.ITypeInfo;
  bstrName : WideString;
begin
   case tdesc.vt of
     VT_PTR   : Result:=GetTypeStr(tdesc.ptdesc^,Context);
     VT_ARRAY : Result:=Format('Array of %s',[GetTypeStr(tdesc.padesc^.tdescElem,Context)]);
     VT_USERDEFINED : begin
                        context.GetRefTypeInfo(tdesc.hreftype, tinfo);
                        tinfo.GetDocumentation(-1, @bstrName, nil, nil, nil);
                        Result:=bstrName;
                      end
   else
     Result:=VarTypeAsText(tdesc.vt);
   end;
end;


//http://msdn.microsoft.com/en-us/magazine/dd347981.aspx
Procedure InspectCOMOnbject(const ClassName: string);
Var
  ComObject     : OleVariant;
  Dispatch      : IDispatch;
  Count         : Integer;
  i,j,k         : Integer;
  Typeinfo      : ActiveX.ITypeinfo;
  ptypeattr     : ActiveX.PTypeAttr;
  pfuncdesc     : ActiveX.PFuncDesc;//http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vswizard.tagfuncdesc.aspx
  rgbstrNames   : TBStrList;
  cNames        : Integer;
  bstrName      : WideString;
  bstrDocString : WideString;
  sValue        : string;
  sinvkind      : string;
begin
  ComObject     := CreateOleObject(ClassName);
  Dispatch      := IUnknown(ComObject) as IDispatch;
  OleCheck(Dispatch.GetTypeInfoCount(Count));
  for i := 0 to Count-1 do
    begin
       OleCheck(Dispatch.GetTypeInfo(i,0,Typeinfo));
       OleCheck(Typeinfo.GetTypeAttr(ptypeattr));
       try
        case ptypeattr^.typekind of
         TKIND_INTERFACE,
         TKIND_DISPATCH :
          begin
            for j:=0 to ptypeattr^.cFuncs-1 do
            begin
               OleCheck(Typeinfo.GetFuncDesc(j, pfuncdesc));
               try
                 OleCheck(Typeinfo.GetNames(pfuncdesc.memid, @rgbstrNames, pfuncdesc.cParams + 1, cNames));
                 OleCheck(Typeinfo.GetDocumentation(pfuncdesc.memid,@bstrName,@bstrDocString,nil,nil));

                 if 1=1 then //pfuncdesc.elemdescFunc.tdesc.vt<>$0018 then
                 begin
                   //pfuncdesc.elemdescFunc.paramdesc
                   case pfuncdesc.invkind of
                    INVOKE_FUNC           : if pfuncdesc.elemdescFunc.tdesc.vt = VT_VOID then sinvkind :='procedure' else sinvkind :='function';
                    INVOKE_PROPERTYGET    : sinvkind :='get property';
                    INVOKE_PROPERTYPUT    : sinvkind :='put property';
                    INVOKE_PROPERTYPUTREF : sinvkind :='ref property';
                   else
                     sinvkind :='unknow';
                   end;


                    {
                   if bstrDocString<>'' then
                    Writeln(Format('// %s',[bstrDocString]));
                     }
                    if pfuncdesc.cParams<=1 then
                    begin
                       if pfuncdesc.elemdescFunc.tdesc.vt = VT_VOID then
                        Writeln(Format('%s %s;',[sinvkind,bstrName]))
                       else
                        Writeln(Format('%s %s : %s;',[sinvkind,bstrName,GetTypeStr(pfuncdesc.elemdescFunc.tdesc, Typeinfo)]));
                    end
                    else
                    begin
                      sValue:='';
                      for k := 1 to pfuncdesc.cParams do
                      begin
                        //Writeln(Format('%s : %d',[rgbstrNames[k], pfuncdesc.lprgelemdescParam[k-1].tdesc.vt]));
                        sValue:= sValue + Format('%s : %s',[rgbstrNames[k], GetTypeStr(pfuncdesc.lprgelemdescParam[k-1].tdesc,Typeinfo)]);
                        if k<pfuncdesc.cParams then
                          sValue:=sValue+';';
                      end;

                      if pfuncdesc.elemdescFunc.tdesc.vt = VT_VOID then
                        Writeln(Format('%s %s (%s);',[sinvkind, bstrName, sValue]))
                      else
                        Writeln(Format('%s %s (%s) : %s;',[sinvkind, bstrName,SValue,GetTypeStr(pfuncdesc.elemdescFunc.tdesc, Typeinfo)]))
                    end;
                      //Writeln(pfuncdesc.elemdescFunc.tdesc.vt);
                 end;
               finally
                 Typeinfo.ReleaseFuncDesc(pfuncdesc);
               end;
            end;
          end;
        end;
       finally
          Typeinfo.ReleaseTypeAttr(ptypeattr);
       end;
    end;
end;



begin
 try
    CoInitialize(nil);
    try
      //InspectCOMOnbject('WbemScripting.SWbemLocator');
      InspectCOMOnbject('Excel.Application');
      //InspectCOMOnbject('Schedule.Service');
      //InspectCOMOnbject('WScript.Shell');
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.
like image 91
RRUZ Avatar answered Sep 28 '22 08:09

RRUZ


The object may implement IDispatchEx, then you can use GetNextDispID to enumerate all members, and GetMemberName and GetMemberProperties to discover some (minimal) information about each member.

Or, the object may implement IDispatch, and in particular IDispatch::GetTypeInfo, then you can (with some difficulty) extract information about its members from ITypeInfo object.

Before you ask, I have no idea how to do any of this in Delphi.

If all else fails, you can read the documentation.

like image 32
Igor Tandetnik Avatar answered Sep 28 '22 08:09

Igor Tandetnik