Search code examples
delphiado

Convert variant of type (Dispatch)


I am trying to use Delphi 7 / ADO to read the lastLogonTimeStamp field in AD for each user. My loop can read the fields required. The string fields are easy to use, but I have failed to decode the timestamp. It is of type Variant and vartype returns 9 (Dispatch)

I've tried various typecasts, including INT64 (which I believe is how the timestamp is stored), and varToStr. All the records are vartype 9 except one which is vartype 1 probably because it is empty.

ADOQuery1: TADOQuery;
ADOQuery1SN: TWideStringField;
ADOQuery1CN: TWideStringField;
ADOQuery1AdsPath: TWideStringField;
ADOQuery1lastLogonTimestamp: TVariantField;
logonfield : variant;

    logonfield := ADOQuery1lastLogonTimestamp.value;
    stringgrid1.Cells[1,i] :=  vartostr(logonfield);

I want to get date of last logon for each user, with exceptions filtered by the program. I can get the string fields OK. But I get:

Error: could not convert type (dispatch) into type (string) [or anything else I tried! TDateTime, INT64 ...]


Solution

  • Variant type 9 (varDispatch) represents a COM IDispatch object interface. Which makes sense in this situation because lastLogonTimeStamp is an Integer8 in FILETIME UTC format, wrapped in a COM object to expose access to its LowPart and HighPart members. See Attributes for AD Users : lastLogonTimestamp and Acitve Directory: Handling attributes with LARGE_INTEGER / INTEGER8 syntax for more details.

    Try something like this:

    function LargeIntegerToDate(value: Variant): TDateTime;
    var
      ftUTC, ftLocal: TFileTime;
      st: TSystemTime;
    begin
      Result := 0;
    
      if VarIsNull(value) then
        Exit;
    
      if not VarIsType(varDispatch) then
        raise Exception.Create('Unsupported type');
    
      ftUTC.dwHighDateTime := value.HighPart;
      ftUTC.dwLowDateTime := value.LowPart;
    
      if (ftUTC.dwLowDateTime = 0) and (ftUTC.dwHighDateTime = 0) then
      begin
        Result := EncodeDate(1601, 1, 1);
        Exit;
      end;
    
      try
        FileTimeToLocalFileTime(ftUTC, ftLocal);
        FileTimeToSystemTime(ftLocal, st);
        Result := SystemTimeToDateTime(st);
      except
      end;
    end;
    
    ...
    
    var
      logonfield : Variant;
    begin
      logonfield := ADOQuery1lastLogonTimestamp.Value;
      StringGrid1.Cells[1, i] := DateToStr(LargeIntegerToDate(logonfield));
    end;