title: How can I intercept function return values in a Delphi Debugger Visualizer?
body: <p>I have a working debugger visualizer that helps visualize variables of type TIntTime.</p>
  
  <p>TIntTime = type integer;</p>
  
  <p>The visualizer replaces a number of seconds since midnight with a time string HH:MM:SS. This works fine on variables of type TIntTime during a debug session, but not on functions. For example if I place GetiTime in a watch</p>
  
  <pre><code>function GetiTime: TIntTime;
  begin
    Result:=30000;
  end;
  </code></pre>
  
  <p>the watch will show 30000. The expected replaced value is '08:20:00'. The visualizer does not intercept function return values of type TIntTime and this is the problem.</p>
  
  <p>I am using Delphi 10 Seattle. My visualizer is based on DateTimeVisualizer.pas found in Delphi 10\source\Visualizers. The DateTimeVisualizer suggests that function return values are intercepted by using the type name string 'function: TIntTime' in GetSupportedType. I have tried </p>
  
  <pre><code>'function: TIntTime'
  'function:TIntTime'
  'function::TIntTime'
  </code></pre>
  
  <p>without luck. I suspect it is a question of getting this type name string correct, but haven't been able to find information about the formatting on the internet.</p>
  
  <p>If I instead place GetDateTime in a watch it shows '14-02-2018 13:20:30' as expected. If I switch the TDateTime/TDate/TTime visualizer off in options the watch shows 43145.5559... This tells me that it is possible to intercept function return values with a visualizer. </p>
  
  <pre><code>  function GetDateTime: TDateTime;
    begin
      Result:=EncodeDateTime(2018,2,14,13,20,30,0);
    end;
  </code></pre>
  
  <p>In my case it is not an option to use the TDateTime data type. So my question is: how can I get my visualizer to intercept function return values of type TIntTime?</p>
  
  <p>Below is the source for the TIntTime visualizer</p>
  
  <pre><code>unit IntTimeVisualizer;
  
  interface
  
  procedure Register;
  
  implementation
  
  uses
    Classes, Forms, SysUtils, ToolsAPI;
  
  resourcestring
    sIntTimeVisualizerName = 'TIntTime Visualizer for Delphi';
    sIntTimeVisualizerDescription = 'Displays TIntTime instances in a human-readable time format rather than as an integer value';
  
  type
    TDebuggerIntTimeVisualizer = class(TInterfacedObject, IOTADebuggerVisualizer,
      IOTADebuggerVisualizerValueReplacer, IOTAThreadNotifier, IOTAThreadNotifier160)
    private
      FCompleted: Boolean;
      FDeferredResult: string;
    public
      { IOTADebuggerVisualizer }
      function GetSupportedTypeCount: Integer;
      procedure GetSupportedType(Index: Integer; var TypeName: string;
        var AllDescendants: Boolean);
      function GetVisualizerIdentifier: string;
      function GetVisualizerName: string;
      function GetVisualizerDescription: string;
      { IOTADebuggerVisualizerValueReplacer }
      function GetReplacementValue(const Expression, TypeName, EvalResult: string): string;
      { IOTAThreadNotifier }
      procedure EvaluteComplete(const ExprStr: string; const ResultStr: string;
        CanModify: Boolean; ResultAddress: Cardinal; ResultSize: Cardinal;
        ReturnCode: Integer);
      procedure ModifyComplete(const ExprStr: string; const ResultStr: string;
        ReturnCode: Integer);
      procedure ThreadNotify(Reason: TOTANotifyReason);
      procedure AfterSave;
      procedure BeforeSave;
      procedure Destroyed;
      procedure Modified;
      { IOTAThreadNotifier160 }
      procedure EvaluateComplete(const ExprStr: string; const ResultStr: string;
        CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
        ReturnCode: Integer);
    end;
  
    TIntTimeType = (dttIntTime);
  
    TIntTimeVisualizerType = record
      TypeName: string;
      TimeType: TIntTimeType;
    end;
  
  const
    IntTimeVisualizerTypes: array[0..1] of TIntTimeVisualizerType =
    (
      (TypeName: 'TIntTime'; TimeType: dttIntTime;),    //&lt;-- This type is working fine
      (TypeName: 'function: TIntTime'; TimeType: dttIntTime;)  //&lt;-- This type is not working
    );
  
  { TDebuggerIntTimeVisualizer }
  
  procedure TDebuggerIntTimeVisualizer.AfterSave;
  begin
    // don't care about this notification
  end;
  
  procedure TDebuggerIntTimeVisualizer.BeforeSave;
  begin
    // don't care about this notification
  end;
  
  procedure TDebuggerIntTimeVisualizer.Destroyed;
  begin
    // don't care about this notification
  end;
  
  procedure TDebuggerIntTimeVisualizer.Modified;
  begin
    // don't care about this notification
  end;
  
  procedure TDebuggerIntTimeVisualizer.ModifyComplete(const ExprStr,
    ResultStr: string; ReturnCode: Integer);
  begin
    // don't care about this notification
  end;
  
  procedure TDebuggerIntTimeVisualizer.EvaluteComplete(const ExprStr,
    ResultStr: string; CanModify: Boolean; ResultAddress, ResultSize: Cardinal;
    ReturnCode: Integer);
  begin
    EvaluateComplete(ExprStr, ResultStr, CanModify, TOTAAddress(ResultAddress),
      LongWord(ResultSize), ReturnCode);
  end;
  
  procedure TDebuggerIntTimeVisualizer.EvaluateComplete(const ExprStr,
    ResultStr: string; CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
    ReturnCode: Integer);
  begin
    FCompleted := True;
    if ReturnCode = 0 then
      FDeferredResult := ResultStr;
  end;
  
  procedure TDebuggerIntTimeVisualizer.ThreadNotify(Reason: TOTANotifyReason);
  begin
    // don't care about this notification
  end;
  
  function TDebuggerIntTimeVisualizer.GetReplacementValue(
    const Expression, TypeName, EvalResult: string): string;
  var
    TimeType: TIntTimeType;
    I: Integer;
  
    function IntTimeToStr(s: Integer): string;
    var
      hh, mm, ss: integer;
    begin
      hh:=s div 3600;
      mm:=(s div 60)-hh*60;
      ss:=s mod 60;
      Result:=Format('%.2d:%.2d:%.2d',[hh,mm,ss]);
    end;
  
    function FormatResult(const LEvalResult: string; DTType: TIntTimeType; out ResStr: string): Boolean;
    var
      IntValue: integer;
    begin
      Result := True;
      try
        if not TryStrToInt(LEvalResult, IntValue) then
          Result:=false
        else
          case DTType of
            dttIntTime: ResStr:=IntTimeToStr(IntValue);
          end;
      except
        Result := False;
      end;
    end;
  
  begin
    TimeType := TIntTimeType(-1);
    for I := Low(IntTimeVisualizerTypes) to High(IntTimeVisualizerTypes) do begin
      if TypeName = IntTimeVisualizerTypes[I].TypeName then begin
        TimeType:=IntTimeVisualizerTypes[I].TimeType;
        Break;
      end;
    end;
  
    if not FormatResult(EvalResult, TimeType, Result) then
      Result := EvalResult;
  end;
  
  function TDebuggerIntTimeVisualizer.GetSupportedTypeCount: Integer;
  begin
    Result := Length(IntTimeVisualizerTypes);
  end;
  
  procedure TDebuggerIntTimeVisualizer.GetSupportedType(Index: Integer; var TypeName: string;
    var AllDescendants: Boolean);
  begin
    AllDescendants := false;
    TypeName := IntTimeVisualizerTypes[Index].TypeName;
  end;
  
  function TDebuggerIntTimeVisualizer.GetVisualizerDescription: string;
  begin
    Result := sIntTimeVisualizerDescription;
  end;
  
  function TDebuggerIntTimeVisualizer.GetVisualizerIdentifier: string;
  begin
    Result := ClassName;
  end;
  
  function TDebuggerIntTimeVisualizer.GetVisualizerName: string;
  begin
    Result := sIntTimeVisualizerName;
  end;
  
  var
    IntTimeVis: IOTADebuggerVisualizer;
  
  procedure Register;
  begin
    IntTimeVis:=TDebuggerIntTimeVisualizer.Create;
    (BorlandIDEServices as IOTADebuggerServices).RegisterDebugVisualizer(IntTimeVis);
  end;
  
  procedure RemoveVisualizer;
  var
    DebuggerServices: IOTADebuggerServices;
  begin
    if Supports(BorlandIDEServices, IOTADebuggerServices, DebuggerServices) then begin
      DebuggerServices.UnregisterDebugVisualizer(IntTimeVis);
      IntTimeVis:=nil;
    end;
  end;
  
  initialization
  
  finalization
    RemoveVisualizer;
  end.
  </code></pre>