问题
Delphi enumeration values are natively defined as unsigned integers - 1, 2 or 4 bytes, depending on the setting of MINENUMSIZE.
I have a case where we are using Variants to transfer data of different data types between applications. All other data types work nicely, but now we realised that the expectation from other applications is that the enumerated values should be signed integers instead of unsigned (and some are really validating this).
Is there a way to configure the automatic variant conversion that the enumeration values would be converted to varInteger, instead of varUInt32?
The background: we are writing library code for OPC UA. OPC UA defines a type called Variant, which has history in Windows Variant, but is now defined in a different way. In practice, OPC UA defines that enumeration values are transferred as Int32 over the wire.
OPC UA also defines a data type called DataValue, which consists of the Value (as Variant), StatusCode (UInt32) and some Timestamp fields.
Now, we are using Delphi Variants for mapping the OPC UA Variant, since it works nicely in general. Now the only major pitfall is that when you write an enumeration value to the Variant it is converted to UInt32, whereas OPC UA expects Int32.
The library takes in Variants (in DataValues) and as an application developer, you can just assign your enumeration values to the DataValue and everything looks good. However, the value has been converted to an UInt32 and when the library code sees this Variant it cannot know anymore that it corresponds actually to an enumeration type of variable.
Should we be able to control the automatic conversion (or actually the native data type of enumerations), we could simply get rid of this problem with that.
If that is not possible, we will really have to invent conversion code, wherever we can, but which can never be 100% certain, after all.
回答1:
You can't distinguish between assignment of enumerated type to variant, and assignment of integer type to variant. Consider the following code:
uses
System.SysUtils;
type
TFoo = (foo, bar);
var
V: Variant;
enum: TFoo;
b: Byte;
begin
V := enum;
V := b;
end.
The compiler generates the following:
Project52946989.dpr.14: V := enum; 00422562 B8389F4200 mov eax,$00429f38 00422567 33D2 xor edx,edx 00422569 8A15489F4200 mov dl,[$00429f48] 0042256F B101 mov cl,$01 00422571 E8BED1FFFF call @VarFromInt Project52946989.dpr.15: V := b; 00422576 B8389F4200 mov eax,$00429f38 0042257B 33D2 xor edx,edx 0042257D 8A15499F4200 mov dl,[$00429f49] 00422583 B101 mov cl,$01 00422585 E8AAD1FFFF call @VarFromInt
So even if you hook the library routine that handles such an assignment (System.Variants._VarFromInt
), you will inevitably pick up assignments of Byte
values as well as enumerated type values.
The above code assumes single byte enumerated type size, but nothing much changes for 2 or 4 byte enumerated types. Instead of the assignment looking like a byte assignment it looks like a Word
or LongWord
assignment, respectively.
来源:https://stackoverflow.com/questions/52946989/delphi-enums-to-variant-as-varinteger-instead-of-varuint32