Получить члены COM-объекта через Тип Delphi Olevariant

можно ли получить список членов (свойств, функций, процедур) для COM-объекта через Тип OleVariant?

например,

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

и мне особенно интересно получить список функций, таких как WScript.Echo, WScript.Бросить и т. д.

Я хочу иметь эту функцию, потому что хорошо реализовать автоматическое завершение кода.

2 ответов


можно использовать GetTypeInfo способ и ITypeInfo интерфейс.

попробуйте этот пример кода (не завершен, но вы можете использовать его в качестве отправной точки)

{$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<>18 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.

объект может реализовать IDispatchEx можно использовать GetNextDispID перечислить все члены и GetMemberName и GetMemberProperties чтобы узнать некоторую (минимальную) информацию о каждом члене.

или объект может реализовать IDispatch и в частности IDispatch::GetTypeInfo, то вы можете (с некоторым трудом) извлечь информацию о своих членах из