9

是否可以通过 OleVariant 类型获取 COM 对象的成员列表(属性、函数、过程)?

例如,

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

我对获取 WScript.Echo、WScript.Quit 等函数列表特别感兴趣。

我想要这个功能,因为它很好地实现了代码自动完成。

4

2 回答 2

8

您可以使用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<>$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.
于 2013-10-30T13:56:20.667 回答
2

该对象可以实现IDispatchEx,然后您可以使用它GetNextDispID来枚举所有成员,并GetMemberName发现GetMemberProperties有关每个成员的一些(最少)信息。

或者,对象可以实现IDispatch,特别是,然后您可以(有一些困难)从对象中IDispatch::GetTypeInfo提取有关其成员的信息。ITypeInfo

在你问之前,我不知道如何在 Delphi 中做这些。

如果一切都失败了,您可以阅读文档

于 2013-10-30T13:32:13.907 回答