2012-10-11 14 views
5

Considera il codice sotto il quale viene compilato ed eseguito senza errori in Delphi 6. Quando ripristino l'array di stringhe dinamiche, invece di vedere un array vuoto in sa, vedo un array con una lunghezza di 1 con un singolo elemento contenente un stringa vuota. Perché questo e come posso assegnare in modo sicuro un array dinamico NIL a un Variant e ripristinarlo correttamente? Ecco il codice:Perché l'assegnazione di un array NIL a un Variant causa la restituzione di un array non vuoto in Delphi 6?

TDynamicStringArray = array of string; 

var 
    V: Variant; 
    sa: TDynamicStringArray; 
begin 
    sa := nil; 

    V := sa; 

    sa := V; 

    OutputDebugString('sa has a single element now with an empty string in it when I expect it to be empty.'); 
end; 
+0

forse typecast è 'variante -> string -> array di strings' piuttosto che' variante -> pointer -> array di strings' –

risposta

5

Ci sono due bug qui.

Prima di tutto in Variants.DynArrayVariantBounds. Quando l'array dinamico è nil restituisce erroneamente una coppia limite basso/alto di (0, 0). Dovrebbe restituire (0, -1). Questo bug è stato risolto nelle ultime versioni di Delphi. Ciò causa che V := sa restituisce un array variante con un elemento singolo, vuoto.

Il secondo errore riguarda l'altra direzione, sa := V. Questo bug è ancora presente nelle ultime versioni di Delphi. Questo errore si trova in Variants.DynArrayFromVariant. Esiste un ciclo repeat/until che passa sopra l'array di varianti di input e popola l'array dinamico di output. Quando l'array della variante di input è vuoto, non deve entrare nel ciclo repeat/until. Tuttavia, il codice lo fa erroneamente e tenta di leggere un elemento dell'array variante con VarArrayGet. Poiché la matrice è vuota, ciò provoca un errore di runtime. Ho segnalato questo: QC#109445.

Ecco un bit di codice molto semplice che corregge i bug. Si noti che ho considerato solo il caso in cui gli array sono unidimensionali. Se è necessario supportare matrici di dimensioni superiori, è possibile estendere questo approccio per farlo.

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    Variants; 

var 
    OriginalVarFromDynArray: procedure(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer); 
    OriginalVarToDynArray: procedure(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); 

function DynArrayVarType(typeInfo: PDynArrayTypeInfo): Integer; 
const 
    tkDynArray = 17; 
begin 
    Result := varNull; 
    if (typeInfo<>nil) and (typeInfo.Kind=tkDynArray) then 
    begin 
    Inc(PChar(typeInfo), Length(typeInfo.name)); 
    Result := typeInfo.varType; 
    if Result=$48 then 
     Result := varString; 
    end; 
    if (Result<=varNull) or (Result=$000E) or (Result=$000F) or ((Result>varInt64) and not (Result=varString)) then 
    VarCastError; 
end; 

procedure VarFromDynArray(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer); 
var 
    VarType, DynDim: Integer; 
begin 
    DynDim := DynarrayDim(PDynArrayTypeInfo(TypeInfo)); 
    if DynDim=1 then 
    begin 
    //only attempt to deal with 1 dimensional arrays 
    if DynArray=nil then begin 
     VarClear(V); 
     VarType := DynArrayVarType(PDynArrayTypeInfo(TypeInfo)); 
     if VarType = varString then 
     VarType := varOleStr; 
     V := VarArrayCreate([0, -1], VarType); 
     exit; 
    end; 
    end; 
    OriginalVarFromDynArray(V, DynArray, TypeInfo); 
end; 

procedure VarToDynArray(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer); 
var 
    DimCount: Integer; 
    Len: Integer; 
begin 
    DimCount:= VarArrayDimCount(V); 
    if DimCount=1 then 
    begin 
    //only attempt to deal with 1 dimensional arrays 
    Len := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1; 
    if Len=0 then begin 
     DynArraySetLength(DynArray, PDynArrayTypeInfo(TypeInfo), 1, @Len); 
     exit; 
    end; 
    end; 
    OriginalVarToDynArray(DynArray, V, TypeInfo); 
end; 

procedure FixVariants; 
var 
    VarMgr: TVariantManager; 
begin 
    GetVariantManager(VarMgr); 
    OriginalVarFromDynArray := VarMgr.VarFromDynArray; 
    VarMgr.VarFromDynArray := VarFromDynArray; 
    OriginalVarToDynArray := VarMgr.VarToDynArray; 
    VarMgr.VarToDynArray := VarToDynArray; 
    SetVariantManager(VarMgr); 
end; 

type 
    TDynamicStringArray = array of string; 

var 
    V: Variant; 
    sa: TDynamicStringArray; 
begin 
    FixVariants; 

    sa := nil; 
    V := sa; 
    sa := V; 

    Writeln(Length(sa)); 
    Readln; 
end. 
+0

Grazie. Questo è un bug davvero importante. –