5

Delphi 6 でエラーなくコンパイルおよび実行される以下のコードを考えてみましょう。動的文字列配列を復元すると、saに空の配列が表示されるのではなく、空の文字列を含む 1 つの要素を持つ長さ 1 の配列が表示されます。これはなぜですか? また、NIL 動的配列をバリアントに安全に割り当てて適切に回復するにはどうすればよいですか? コードは次のとおりです。

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;
4

1 に答える 1

5

ここには2つのバグがあります。

まず第一にVariants.DynArrayVariantBounds。動的配列の場合、nilこれは誤っての下限/上限のペアを返します(0, 0)。を返す必要があり(0, -1)ます。このバグは、Delphiの最新バージョンで修正されています。これによりV := sa、単一の空の要素を持つバリアント配列が返されます。

2番目のバグは、他の方向に影響しsa := Vます。このバグは、Delphiの最新バージョンにも引き続き存在します。このバグはにありVariants.DynArrayFromVariantます。repeat/until入力バリアント配列をウォークオーバーし、出力動的配列にデータを入力するループがあります。repeat/until入力バリアント配列が空の場合、そのループに入らないようにする必要があります。ただし、コードは誤ってこれを行い、バリアント配列の要素を。で読み取ろうとしますVarArrayGet。配列が空であるため、実行時エラーが発生します。私はこれを報告しました:QC#109445

これは、バグを修正する非常に簡単なコードです。配列が1次元の場合のみを考慮していることに注意してください。高次元の配列をサポートする必要がある場合は、このアプローチを拡張してサポートできます。

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.
于 2012-10-11T08:44:20.593 に答える