Hallo Matthias and Peter,

> >(3) As I wrote above, I want to use Cach� objects in a dynamical way. So
if
> >I use the factory ActiveX control to gve me an ActiveX ORef: is there any
> >way to set the value of a property the name of which is only known at
> >runtime?

> Difficult one this - one way is from a Delphi string ( the name of the
> property ) is to interrogate the Factory ActiveX which then returns
> the address and then you can call it
> I believe this is possible but haven't worked out the details yet

Maybe some fragments of my code could help you?

I solved same problem in following manner:

(1) I asked Cache %CompiledClass for property names (and queries, methods
etc.) of Cach� class (say Person);
(2) saved results as metadata to my special metadata holder Delphi classes
(TCacheClassDef, TCachePropertyDef, TCacheMethodDef etc.).
(3) used that metadata when needed to set value of property or read value of
property by name:

First, some generic code, dealing a little tricks with part of COM stuff:
****
uses
  ActiveX, ComObj, ComConst, Consts, TypInfo;

{ Dispatch helpers }

{function GetIDsOfNames is copied from implementation section of ComObj.}

function GetIDsOfNames(const Dispatch: IDispatch; Names: PChar;
  NameCount: Integer; DispIDs: PDispIDList): HResult;
begin
...
  {Cannot show to public some parts of secret Borland code :-) }
...
end;

My own parts of generic code isn't so secret:

{ Get/Set raw Dispatch properties }

const
  DispIDArgs: Longint = DISPID_PROPERTYPUT;

{Gets value for property with DispId, IDispatch is factory interface
(IFactory from
 CacheObject_TLB.pas)}
function GetDispatchPropValue(Disp: IDispatch; DispID: Integer): OleVariant;
var
  ExcepInfo: TExcepInfo;
  DispParams: TDispParams;
  Status: HResult;
begin
  FillChar(DispParams, SizeOf(DispParams), 0);
  Status := Disp.Invoke(DispID, GUID_NULL, 0, DISPATCH_PROPERTYGET,
DispParams,
    @Result, @ExcepInfo, nil);
  if Status <> S_OK then DispatchInvokeError(Status, ExcepInfo);
end;

{Sets value for property with DispId}
procedure SetDispatchPropValue(Disp: IDispatch; DispID: Integer; const
Value:
  OleVariant);
var
  ExcepInfo: TExcepInfo;
  DispParams: TDispParams;
  Status: HResult;
begin
  with DispParams do
  begin
    rgvarg := @Value;
    rgdispidNamedArgs := @DispIDArgs;
    cArgs := 1;
    cNamedArgs := 1;
  end;
  Status := Disp.Invoke(DispId, GUID_NULL, 0, DISPATCH_PROPERTYPUT,
DispParams,
    nil, @ExcepInfo, nil);
  if Status <> S_OK then DispatchInvokeError(Status, ExcepInfo);
end;

**********************************
Use previous generic procedures in your business classes:

function TMyCacheCustomObject.GetPropValueByName(const aPropName: string):
Variant;
var
  DispId: Integer;
  PDef: TMyCachePropertyDef;
begin
  {Get property definition meta}
  PDef := PropertyDef(aPropName);

  {If DispId for aPropName is not saved from previous run, then ask for
it...}
  if PDef.DispId = const_UnknownDispId then
  begin
    <ThisUnit>.GetIdsOfNames(FObj, PChar(aPropName), 1, @DispId);
    {... and save it for next time}
    PDef.DispId := DispId;
  end
  else
    {...else use saved DispId from previous run}
    DispId := PDef.DispId;

  {FObj is IFactory (yes, my TMyCacheCustomObject knows its factory),
   DispId is found, so get property value}
  result := GetDispatchPropValue(FObj, DispId);

end;

procedure TMyCacheCustomObject.SetPropValueByName(const aPropName: string;
aPropValue: Variant);
var
  DispId: Integer;
  PDef: TMyCachePropertyDef;
begin
  PDef := PropertyDef(aPropName);

  if PDef.DispId = c_UnknownDispId then
  begin
    <ThisUnit>.GetIdsOfNames(FObj, PChar(aPropName), 1, @DispId);
    PDef.DispId := DispId;
  end
  else
    DispId := PDef.DispId;

  SetDispatchPropValue(FObj, DispId, aPropValue);

end;

(*** end of example code ***)

That above are some parts of my Cach� Components library, which probably
never be finished, because I use Cach� only as great hobby, my real job is
Delphi, not M :-(.

Milan




Reply via email to