首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >删除Graphics32图层时崩溃

删除Graphics32图层时崩溃
EN

Stack Overflow用户
提问于 2017-05-13 18:17:35
回答 1查看 193关注 0票数 4

在尝试使用Graphics32删除图层时,我遇到了一个问题。似乎除非您以相反的顺序删除图层(从最后添加到第一个),否则会抛出异常。我创建了最简单的应用程序来测试这一点,并且每次都是可重复的。

我创建了一个简单的表单,其中包含一个TImgView32组件(默认情况下都是属性),然后是一个执行以下操作的按钮:

代码语言:javascript
复制
procedure TMainForm.btnDeleteTestClick(Sender: TObject);
var
  Layer1: TCustomLayer;
  Layer2: TCustomLayer;
begin
  Layer1 := TCustomLayer.Create(ImageView.Layers);
  Layer2 := TCustomLayer.Create(ImageView.Layers);

  Layer1.Free;
  Layer2.Free;
end;

如果我颠倒顺序(Layer2.Free,然后是Layer1.Free),它工作得很好,但这种方式每次都会崩溃。无论我使用的是TCustomLayer、TPositionedLayer、TBitmapLayer还是其他什么,都是一样的。

我已经遍历了错误,错误似乎起源于这里:

代码语言:javascript
复制
function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
begin 
  with FBuckets[BucketIndex] do begin 
    Result := Items[ItemIndex].Data; 
    if FCount = 0 then Exit; 
    Dec(Count); 
    if Count = 0 then SetLength(Items, 0) 
    else if (ItemIndex < Count) then 
      Move(Items[ItemIndex + 1], Items[ItemIndex], (Count - ItemIndex - 1) * SizeOf(TPointerBucketItem)); 
  end; 
  Dec(FCount); 
end;

你知道是什么原因造成的吗?还是我做错了什么?顺便说一下,我正在运行Delphi XE。

EN

回答 1

Stack Overflow用户

发布于 2017-05-14 20:51:10

下面是TCustomLayer.Destroy的代码

代码语言:javascript
复制
destructor TCustomLayer.Destroy;
var
  I: Integer;
begin
  if Assigned(FFreeNotifies) then
  begin
    for I := FFreeNotifies.Count - 1 downto 0 do
    begin
      TCustomLayer(FFreeNotifies[I]).Notification(Self);
      if FFreeNotifies = nil then Break;
    end;
    FFreeNotifies.Free;
    FFreeNotifies := nil;
  end;
  SetLayerCollection(nil);  <<-- bug, see below.
  inherited;  <<---- See note below.
end;

请注意,SetLayerCollection中有一个错误。

错误代码

代码语言:javascript
复制
procedure TCustomLayer.SetLayerCollection(Value: TLayerCollection);
begin
  if FLayerCollection <> Value then begin
    if Assigned(FLayerCollection) then begin
      if FLayerCollection.MouseListener = Self then
        FLayerCollection.MouseListener := nil;
      FLayerCollection.RemoveItem(Self);
    end;
    if Assigned(Value) then Value.InsertItem(Self);
  end;
  /// FLayerCollection is never set!
end;

SetLayerCollection(nil);行实际上并没有设置LayerCollection!内部FLayerCollection可能会受到use after free条件的影响,这可能就是发生在您身上的情况。

如下所示更改SetLayerCollection的代码:

错误修复

代码语言:javascript
复制
procedure TCustomLayer.SetLayerCollection(Value: TLayerCollection);
begin
  if FLayerCollection <> Value then begin
    if Assigned(FLayerCollection) then begin
      if FLayerCollection.MouseListener = Self then begin
        FLayerCollection.MouseListener := nil;
      end;
      FLayerCollection.RemoveItem(Self);
    end;
    if Assigned(Value) then begin
      Value.InsertItem(Self)
    end;
    FLayerCollection:= Value;  // add this line.
  end;
end;

便笺

我的假设是以下代码片段导致了错误:

代码语言:javascript
复制
  SetLayerCollection(nil);
  inherited;

SetLayerCollection(value);保持FLayerCollection不变。

inherited析构函数以某种方式调用了与LayerCollection有关的内容。

如果这修复了错误,请让我知道。

我已经提交了一个新的问题:https://github.com/graphics32/graphics32/issues/13

更新:问题因TPointerMap.Delete中的一个错误而关闭

真正的问题在这里:https://github.com/graphics32/graphics32/issues/14

TPointerMap.Delete的代码不正确:

代码语言:javascript
复制
function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
begin
  with FBuckets[BucketIndex] do
  begin
    Result := Items[ItemIndex].Data;

    if FCount = 0 then Exit;   <<-- error: how can result be valid if count = 0?

    Dec(Count);
    if Count = 0 then
      SetLength(Items, 0)
    else
    if (ItemIndex < Count) then
       //Oops off by 1 error!  ---------------------------------------VVVVV
      Move(Items[ItemIndex + 1], Items[ItemIndex], (Count - ItemIndex - 1) * SizeOf(TPointerBucketItem));
  end;
  Dec(FCount); <<-- The use of with makes this statement confusing.
end;

代码应按如下方式更改:

代码语言:javascript
复制
function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
var
  Bucket: TPointerBucket ;
begin
    if FCount = 0 then Exit(nil);
    //Perhaps add some code to validate BucketIndex & ItemIndex?
    Assert(BucketIndex < Length(FBuckets));
    Bucket:= FBuckets[BucketIndex];
    if ItemIndex >= Bucket.
    Assert(ItemIndex < Length(Bucket.Items));
    Result := Bucket.Items[ItemIndex].Data;
    Dec(Bucket.Count);
    if Bucket.Count = 0 then
      SetLength(Bucket.Items, 0)
    else
     /// assume array like so: 0 1 2 3 4  , itemindex = 0
    /// result should be 1 2 3 4
    /// move(1,0,4) (because 4 items should be moved.
    /// Thus move (itemindex+1, intemindex, count-itemindex)
    if (ItemIndex < Bucket.Count) then
      Move(Items[ItemIndex + 1], Items[ItemIndex], (Bucket.Count - ItemIndex) * SizeOf(TPointerBucketItem));
  end;
  Dec(FCount);
end;
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43951924

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档