【问题标题】:Why does assigning a NIL array to a Variant cause a non-empty array to be returned in Delphi 6?为什么将 NIL 数组分配给 Variant 会导致在 Delphi 6 中返回非空数组?
【发布时间】:2012-10-11 01:17:35
【问题描述】:

考虑下面的代码,它在 Delphi 6 中编译和运行没有错误。当我恢复动态字符串数组时,我在 sa 中没有看到一个空数组,而是看到一个长度为 1 的数组具有包含空字符串的单个元素。为什么会这样?如何安全地将 NIL 动态数组分配给 Variant 并正确恢复它?代码如下:

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;

【问题讨论】:

  • 也许类型转换是 variant -> string -> array of strings 而不是 variant -> pointer -> array of strings

标签: delphi dynamic-arrays variant delphi-6


【解决方案1】:

这里有两个错误。

首先在Variants.DynArrayVariantBounds。当动态数组为nil 时,这会错误地返回(0, 0) 的下限/上限对。它应该返回(0, -1)。此错误已在最新版本的 Delphi 中修复。这会导致 V := sa 返回一个带有单个空元素的变体数组。

第二个错误影响另一个方向,sa := V。这个错误仍然存​​在于最新版本的 Delphi 中。这个错误在Variants.DynArrayFromVariant 中。有一个repeat/until 循环遍历输入变量数组并填充输出动态数组。当输入变量数组为空时,它不应该进入那个repeat/until 循环。但是,代码错误地这样做并尝试使用VarArrayGet 读取变量数组的元素。由于数组为空,因此会引发运行时错误。我已经报告了这个:QC#109445

这是修复错误的非常简单的代码。请注意,我只考虑了数组是一维的情况。如果您需要支持更高维的数组,那么您可以扩展此方法。

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.

【讨论】:

  • 谢谢。这是一个非常严重的错误。
猜你喜欢
  • 2011-06-25
  • 2018-05-15
  • 2021-12-06
  • 1970-01-01
  • 2014-02-24
  • 2020-12-31
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多