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.