SafeGetEnumName a safer implementation of TypInfo.GetEnumName

I just had the need for a safer version of Delphi’s GetEnumName, which checks whether the enum value passed to it is actually valid. This is what I came up with:

// P must point to the length field (that is the first byte) of a ShortString
function AfterShortString(const P: Pointer): Pointer; inline;
begin
  Result := Pointer(IntPtr(P) + PByte(P)^ + 1);
end;

function SafeGetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;
var
  P: Pointer;
  T: PTypeData;
begin
  if TypeInfo^.Kind = tkInteger then begin
    Result := IntToStr(Value);
    Exit;
  end;
  T := GetTypeData(GetTypeData(TypeInfo)^.BaseType^);
  if (TypeInfo = System.TypeInfo(Boolean)) or (T^.MinValue < 0) then begin
    // LongBool/WordBool/ByteBool have MinValue < 0 and
    // arbitrary content in Value; Boolean has Value
    // in [0, 1] }
    Result := BooleanIdents[Value <> 0];
    if SameText(HexDisplayPrefix, '0x') then
      Result := LowerCase(Result);
  end else begin
    if (Value < T.MinValue) or (Value > T.MaxValue) then
      Result := Format('OutOfBounds(%d)', [Value])
    else begin
      P := @T^.NameList;
      while Value <> 0 do begin
        P := AfterShortString(P);
        Dec(Value);
      end;
{$IFDEF UNICODE}
      Result := _UTF8ToString(P);
{$ELSE}
      Result := PShortString(P)^;
{$ENDIF}
    end;
  end;
end;

The difference to the original is the IF statement in line 25 that checks whether Value is < T.MinValue or > T.MaxValue. If the value is invalid, the function returns ‘OutOfBounds(<value>)’ similar to the output of the IDE’s debug evaluator, otherwise it returns the name of the enum value. In the original this check is missing, it just assumes that the value parameter is valid, which causes it to return some garbage from memory after the the actual enum value names, or the name of the first enum value entry if Value < T.MinValue. The code should be compatible to most Delphi versions, but I have only tested it with Delphi 2007 and 10.2. This function is now in unit u_dzTypInfo of my dzlib.

Discussion about this post in the international Delphi Praxis forum.