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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With