代码基本上是一样的,直接写就出错;把代码写成过程调用就可以,怪了//出错代码:
procedure TForm1.btn3Click(Sender: TObject);
var
  MS:TMemoryStream;
  P:Pointer;
begin
  MS:=TMemoryStream.Create;
  try
    P:=VarArrayLock(TCustomClientDataSet(ds1.DataSet).Data);
    try
      MS.Write(P^,VarArrayHighBound(TCustomClientDataSet(ds1.DataSet).Data,1)+1)
    finally
      //*******这里出错****************
      //Variant of safe array is locked
      VarArrayUnlock(TCustomClientDataSet(ds1.DataSet).Data);
    end;
    MS.SaveToFile(ExtractFilePath(ParamStr(0))+'Rec.dat');
  finally
    FreeAndNil(MS);
  end;
end;//可以正常使用的代码
procedure TForm1.btn3Click(Sender: TObject);
var
  MS:TMemoryStream;
begin
  TCustomClientDataSet(ds1.DataSet).LogChanges:=False;
  MS:=TMemoryStream.Create;
  try
      //*************************************
      //这样可以:
    VariantToStream(TCustomClientDataSet(ds1.DataSet).Data,MS);
    MS.SaveToFile(ExtractFilePath(ParamStr(0))+'Rec.dat');
  finally
    FreeAndNil(MS);
  end;
end;//附上转换过程:
procedure VariantToStream(const Data:OleVariant;Stream:TStream);
var
  P:Pointer;
begin
  P:=VarArrayLock(Data);
  try
    Stream.Write(P^,VarArrayHighBound(Data,1)+1);
  finally
    VarArrayUnlock(Data);
  end;
end;

解决方案 »

  1.   

    不要直接“TCustomClientDataSet(ds1.DataSet).Data”这样子引用,否则会在过程当中存在一个不可见的中间变量,而这个Variant型的变量需要在整个过程/函数结束之后才会被释放。而你写的过程则刚好是因为使用了一个显式的中间变量"const Data:OleVariant",其生命周期是可预测的,所以不会再产生类似的问题。
      

  2.   

    //这样写也会出错,(cds1: TClientDataSet),是不是cds1.data的地址每次会变化造成的?
    procedure TForm1.btn5Click(Sender: TObject);
    var
      MS:TMemoryStream;
      P:Pointer;
    begin
      MS:=TMemoryStream.Create;
      try
        P:=VarArrayLock(cds1.Data);
        try
          MS.Write(P^,VarArrayHighBound(cds1.Data,1)+1)
        finally
          //*******这里出错****************
          //Variant of safe array is locked
          VarArrayUnlock(cds1.Data);
        end;
        MS.SaveToFile(ExtractFilePath(ParamStr(0))+'Rec.dat');
      finally
        FreeAndNil(MS);
      end;
    end;
      

  3.   

    //cds1的地址好像不是变化的:
    procedure TForm1.btn5Click(Sender: TObject);
    var
      v:Variant;
    begin
      V:=cds1.Data;
      Caption:=IntToHex(Integer(@V),8);
      V:=cds1.Data;
      Caption:=Caption+':'+IntToHex(Integer(@V),8);
    end;
      

  4.   

    就不能定义一个中间变量,非得使用 xx.data?
      

  5.   

    你的意思是说ClientDataSet.Data的生命期是不确定的(ClientDataSet随时可能Free)吗
      

  6.   

    如果是CB代码,则比较好解析些。代码1:
    VarArrayLock(TCustomClientDataSet(ds1.DataSet).Data);
    VarArrayUnlock(TCustomClientDataSet(ds1.DataSet).Data);
    代码2:
    const Data:OleVariant;
    Data = TCustomClientDataSet(ds1.DataSet).Data ;
    VarArrayLock(Data);
    VarArrayUnlock(Data);
    //这两段代码是不一样的。
    属性Data , 类型是OleVariant . 取属性时,会生成一个临时对象 OleVariant .
    VarArrayLock 一个临时OleVariant对象,然后被释放,这没有影响,但随后VarArrayUnlock一个没有被Lock的临时对象就出错了。中间用一个Data将属性值复制出来后,就没有这个问题。BCB的代码,会很清楚地知道是指针还是引用还是对象。而delphi代码则要依据对象的性质来决定是指针还是对象。对象的生命期特性没有CB代码那么明确。我试图这样解析,再静听后面高人讲解。