Friday, November 02, 2007

Enumerate Optional Parameters of TClientDataSet instance

We may use GetOptionalParam and SetOptionalParam methods to access the optional parameters in TClientDataSet instance. However, if we do not know ahead of what parameters are available, these 2 methods serve no purpose to enumerate a list of available parameters. Unit DBClient.pas defines the following:
type
TCustomClientDataSet = class(TWideDataSet)
private
 FDSBase: IDSBase;
protected
 property DSBase: IDSBase read FDSBase write FDSBase;
end;

TClientDataSet = class(TCustomClientDataSet)
end;
Unit DSInst.pas defines the following:
  DSProps = packed  record
  szName           : MIDASPATH;      { Name, if any }
  iFields          : Integer;      { Number of columns }
  iRecBufSize      : Integer;      { Size of record buffer }
  iBookMarkSize    : Integer;      { Size of bookmark }
  bReadOnly        : LongBool;         { Dataset is not updateable }
  iIndexes         : Integer;      { Number of indexes on dataset }
  iOptParams       : Integer;      { Number of optional parameters }
  bDelta           : LongBool;         { This is a delta dataset }
  iLCID            : Integer;      { Language used }
  iUnused          : packed array[0..7] of Integer; { Reserved }
end;


  function GetOptParameter(       { Returns optional parameter (unknown to dataset) }
      iNo      : LongWord;           { Number 1..iOptAttr }
      iFldNo   : LongWord;           { 0 if not field attribute }
  var ppName   : Pointer;         { returns ptr to name }
  var piType   : LongWord;           { returns type }
  var piLen    : LongWord;           { returns length }
  var ppValue  : Pointer          { returns ptr to value }
  ): DBResult; stdcall;

We may attempt to use the above definition to enumerate a list of available optional parameters in TClientDataSet instance. Before we proceed, there is an obstacle to solve. The DSBase property is protected. To overcome it, we can define a class helper function to access the protected property.
type
TClientDataSetHelper = class helper for TClientDataSet
public
  function GetDSBase: IDSBase;
end;

function TClientDataSetHelper.GetDSBase: IDSBase;
begin
Result := DSBase;
end;
The following code shows how to retrieve available optional params.
var D: TClientDataSet;
 p: DSProps;
 pName, pValue: PChar;
 pType, pLen: LongWord;
 iResult: word;
begin
D := TClientDataSet.Create(nil);
try
 D.FieldDefs.Add('Name', ftString, 20);
 D.CreateDataSet;

 D.SetOptionalParam('Param1', 'FirstValue', True);
 D.SetOptionalParam('Param2', 'SecondValue', True);

 ZeroMemory(@p, SizeOf(p));
 D.GetDSBase.GetProps(p);
 Assert(p.iOptParams = 2);

 pName := nil;
 pValue := nil;
 iResult := D.GetDSBase.GetOptParameter(1, 0, pointer(pName), pType, pLen, pointer(pValue));
 Assert(iResult = 0);
 Assert(string(pName) = 'Param1');
 Assert((pType and dsTypeBitsMask) shr dsSizeBitsLen = dsfldZSTRING);
 Assert(pLen = 11);
 Assert(string(pValue) = 'FirstValue');

 pName := nil;
 pValue := nil;
 iResult := D.GetDSBase.GetOptParameter(2, 0, pointer(pName), pType, pLen, pointer(pValue));
 Assert(iResult = 0);
 Assert(string(pName) = 'Param2');
 Assert((pType and dsTypeBitsMask) shr dsSizeBitsLen = dsfldZSTRING);
 Assert(pLen = 12);
 Assert(string(pValue) = 'SecondValue');
finally
 D.Free;
end;
end;