【问题标题】:Access violation assigning autocomplete strings to将自动完成字符串分配给的访问冲突
【发布时间】:2015-01-21 10:21:57
【问题描述】:

我正在从这里修改带有自动完成功能的编辑控件: Auto append/complete from text file to an edit box delphi

我想从数据库加载自动完成字符串。我在自动完成控件后代上声明了新属性:

FACDataSource : TDataSource;
FACFieldName : string;

我称之为加载自动完成字符串:

procedure TAutoCompleteEdit.ReadSuggestions;
begin
  FAutoCompleteSourceList.Clear;

  if (not Assigned(FACDataSource)) or (not Assigned(FACDataSource.DataSet))       or (not ACEnabled) then
      exit;

    with FACDataSource.DataSet do
    begin
       if Active and (RecordCount > 0) and (FACFieldName <> '') then
       begin
         First;
         while not EOF do
         begin
                       FAutoCompleteSourceList.Add(FACDataSource.DataSet.FieldByName(FACFieldName).AsString);
    Next;
  end;
  if FAutoCompleteSourceList.Count > 0 then
    ACStrings := FAutoCompleteSourceList;
end;

结束; 结束;

但是,在将 FAutoCompleteSourceList 分配给 ACStrings 时,我会遇到 AccessViolation。 ACStrings 的设置器是:

procedure TAutoCompleteEdit.SetACStrings(const Value: TStringList);
begin
    if Value <> FACList.FStrings then
      FACList.FStrings.Assign(Value);
end;

我在以下行中收到 AccessViolation:FACList.FStrings.Assign(Value);(读取地址 XXXYYY)。 Value 已定义,此时不是垃圾(例如,我可以在调试器中查看字符串列表)。 'FStrings' 是一个空字符串列表。

将控件放在窗体上时它可以正常工作。但是,如果我将它放在用户输入 DBGridEH 单元格时显示的自定义就地编辑器中,则不会。

就地编辑器是这样的:

    unit UInplaceAutoCompleteEditor;

    interface
    uses UDBAutoComplete, UMyInplaceEditor, classes, windows, Controls, Buttons,     DB;

    type TInplaceAutoCompleteEditor = class(TMyInplaceEditor)
      private
       FEditor : TAutoCompleteEdit;
    FButton : TSpeedButton;
    FShowButton : boolean;
    procedure SetShowButton(value : boolean);
    public
        constructor Create(AOwner : TComponent); override;
        procedure SetFocus; override;
        destructor Destroy; override;
     protected
    procedure EditorKeyDown(Sender : TObject; var Key : Word; Shift : TShiftState);
    function GetACDataSource : TDataSource;
    procedure SetACDataSource(value : TDataSource);
    function GetACFieldName : string;
    procedure SetACFieldName(value : string);
    procedure SetACEnabled(value : boolean);
    function GetACEnabled : boolean;
  published
    property Editor : TAutoCompleteEdit read FEditor;
    property ACDataSource : TDataSource read GetACDataSource write SetACDataSource;
    property ACFieldName : string read GetACFieldName write SetACFieldName;
    property ACEnabled : boolean read GetACEnabled write SetACEnabled;
    property Button : TSpeedButton read FButton;
    property ShowButton : boolean read FShowButton write SetShowButton;
end;

  procedure Register;

implementation

  procedure Register;
  begin
    RegisterComponents('nikolaev', [ TInplaceAutoCompleteEditor ]);
  end;

{ TInplaceAutoCompleteEditor }



constructor TInplaceAutoCompleteEditor.Create(AOwner: TComponent);
begin
  inherited;
  FEditor := TAutoCompleteEdit.Create(self);
  FEditor.Parent := self;
  FEditor.Align := alClient;
  FEditor.Visible := true;
  FEditor.WantTabs := true;
  FEditor.OnKeyDown := EditorKeyDown;

  FButton := TSpeedButton.Create(self);
  FButton.Parent := self;
  FButton.Align := alRight;

  self.FOwnHeight := -1;
  self.FOwnWidth := -1;

  SetShowButton(false);
end;

destructor TInplaceAutoCompleteEditor.Destroy;
begin
  Feditor.Destroy;
  FButton.Destroy;
  inherited;
end;

procedure TInplaceAutoCompleteEditor.EditorKeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if Key in [ VK_Return, VK_Tab ] then
  begin
    self.Value := FEditor.Text;
    Key := 0;
    ConfirmValue;
  end;

  if Key = VK_Escape then
  begin
    Key := 0;
    CancelValue;
  end;

  inherited;
end;



function TInplaceAutoCompleteEditor.GetACDataSource: TDataSource;
begin
  Result := FEditor.ACDataSource;
end;

function TInplaceAutoCompleteEditor.GetACEnabled: boolean;
begin
  Result := FEditor.ACEnabled;
end;

function TInplaceAutoCompleteEditor.GetACFieldName: string;
begin
  Result := FEditor.ACFieldName
end;

procedure TInplaceAutoCompleteEditor.SetACDataSource(value: TDataSource);
begin
  FEditor.ACDataSource := value;
end;

procedure TInplaceAutoCompleteEditor.SetACEnabled(value: boolean);
begin
  FEditor.ACEnabled := value;
end;

procedure TInplaceAutoCompleteEditor.SetACFieldName(value: string);
begin
  FEditor.acfieldname := value;
end;

procedure TInplaceAutoCompleteEditor.SetFocus;
begin
  inherited;
  FEditor.SetFocus;
end;

procedure TInplaceAutoCompleteEditor.SetShowButton(value: boolean);
begin
  if value <> FShowButton then
  begin
    FShowButton := value;
    FButton.Visible := value;
  end;
end;

end.

这个就地编辑器继承自一个抽象类,如下所示:

unit UMyInplaceEditor;

interface
uses Windows, classes, types, dbGridEh, ExtCtrls, Controls;

type TMyInplaceEditor = class (TWinControl)

  private
    FOnValueConfirmed : TNotifyEvent;
    FOnCanceled : TNotifyEvent;
    FWantTabs : boolean;
    procedure AdjustPosition;
  protected
    FOwnHeight, FOwnWidth : integer;
    FValue : Variant;
    function GetIsEditing : boolean;
    procedure SetIsEditing(value : boolean); virtual;
    procedure ConfirmValue;
    procedure CancelValue;
    procedure SetValue(val : Variant); virtual;
  public

    property OnValueConfirmed : TNotifyEvent read FOnValueConfirmed write FOnValueConfirmed;
    property OnCanceled : TNotifyEvent read FOnCanceled write FOnCanceled;
    property Value : Variant read FValue write SetValue;


    property IsEditing : boolean read GetIsEditing write SetIsEditing;
    procedure SetPosition(parentControl : TWinControl; rect : TRect); virtual;

    function ColumnEditable(column : TColumnEH) : boolean; virtual;

    constructor Create(AOwner : TComponent); override;
    property WantTabs : boolean read FWantTabs write FWantTabs;
end;
  procedure Register;

implementation

  procedure Register;
  begin
    RegisterComponents('nikolaev', [TMyInplaceEditor]);
  end;

constructor TMyInplaceEditor.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  self.AutoSize := false;
  self.Visible := false;
  self.FOwnHeight := -1;
  self.FOwnWidth := -1;
end;

procedure TMyInplaceEditor.AdjustPosition;
var xOffset, yOffset : integer;
begin
  xoffset := self.Left + self.Width - self.Parent.Width;
  if xOffset > 0 then
    self.Left := self.Left - xOffset;

  yOffset := self.Top + self.Height - self.Parent.height;
  if yOffset > 0 then
    self.Top := self.Top - yOffset;

end;

function TMyInplaceEditor.GetIsEditing : boolean;
begin
  Result := self.Visible;
end;

procedure TMyInplaceEditor.SetIsEditing(value: Boolean);
begin
  self.Visible := value;
  self.BringToFront;
  {if Visible then
    self.SetFocus;}
end;

procedure TMyInplaceEditor.SetPosition(parentControl : TWinControl; rect: TRect);
begin
  self.Parent := parentControl;
  self.Top := rect.Top;//parentControl.Top;
  self.Left := rect.Left;//parentControl.left;
  if self.FOwnWidth = -1 then
    self.Width := rect.Right - rect.Left
  else
    self.Width := self.FOwnWidth;

    if self.FOwnHeight = -1 then
    self.Height := rect.Bottom - rect.Top
  else
    self.Height := self.FOwnHeight;
  AdjustPosition;
end;

function TMyInplaceEditor.ColumnEditable(column : TColumnEH) : boolean;
begin
  Result := true;
end;

procedure TMyInplaceEditor.ConfirmValue;
begin
  if Assigned(FOnValueConfirmed) then
    FOnValueConfirmed(self);
end;

procedure TMyInplaceEditor.CancelValue;
begin
  if Assigned(FOnCanceled) then
    FOnCanceled(self);
end;

procedure TMyInplaceEditor.SetValue(val : Variant);
begin
  FValue := val;
end;

end.

InplaceEditor 用于DBGridEH 的后代。在某些情况下,我会覆盖 ShowEditor 和 HideEditor 以显示/隐藏我的编辑器。

同样,自动完成控件仅在嵌入到 inplaceeditor 控件时引发异常。

什么导致访问冲突?

【问题讨论】:

    标签: delphi autocomplete controls


    【解决方案1】:

    问题在于您使用的代码错误地处理了接口引用计数。以下是相关摘录:

    type
      TEnumString = class(TInterfacedObject, IEnumString)
      ....
    

    请注意,该类派生自 TInterfacedObject,因此它使用引用计数来管理其生命周期。

    然后代码是这样的:

    type
      TAutoCompleteEdit = class(TEdit)
      private
        FACList: TEnumString;
      ....
    

    所以我们将持有对对象而不是接口的引用。这看起来已经很可疑了。

    然后我们这样做:

    constructor TAutoCompleteEdit.Create(AOwner: TComponent);
    begin
      inherited;
      FACList := TEnumString.Create;
      ....
    end;
    
    destructor TAutoCompleteEdit.Destroy;
    begin
      FACList := nil;
      inherited;
    end;
    

    这里没有任何东西可以让对象保持活力。在代码的其他地方,我们引用了IEnumString 接口。但是一旦该引用被释放,对象就会认为没有任何引用了。所以它被删除了。然后,稍后,代码引用了FACList,它现在指向一个已被销毁的对象。


    解决此问题的一种简单方法是确保TAutoCompleteEdit 控件始终持有对接口的引用:

    type
      TAutoCompleteEdit = class(TEdit)
      private
        FACList: TEnumString;
        FEnumString: IEnumString;
    ....
    constructor TAutoCompleteEdit.Create(AOwner: TComponent);
    begin
      inherited;
      FACList := TEnumString.Create;
      FEnumString := FACList;
      ....
    end;
    

    通过此更改,您可以删除 TAutoCompleteEdit 的析构函数,因为 FEnumString 后面的对象将被引用计数机制销毁。


    解决此问题的另一种方法是更改​​ TEnumString 以禁用自动引用计数。看起来像这样:

    type
      TEnumString = class(TObject, IInterface, IEnumString)
      private
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
        ....
      end;
    
    function TEnumString.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      if GetInterface(IID, Obj) then
        Result := 0
      else
        Result := E_NOINTERFACE;
    end;
    
    function TEnumString._AddRef: Integer; 
    begin
      Result := -1;
    end;
    
    function TEnumString._Release: Integer; 
    begin
      Result := -1;
    end;
    

    然后你需要 TAutoCompleteEdit 析构函数看起来像这样:

    destructor TAutoCompleteEdit.Destroy;
    begin
      FACList.Free;
      inherited;
    end;
    

    最后的选择是完全避免持有TEnumString,而只持有IEnumString 引用。让引用计数像第一个解决方案一样管理生命周期。但是你需要实现另一个接口,允许TAutoCompleteEdit 获取TStrings 对象。

    【讨论】:

    • @KenWhite 您的链接答案在其他方面非常好。如果你能修复那里的代码,那可能是最好的。
    • @David:完成,谢谢。 (我选择了你的第一个建议,因为在我看来它是最干净的。)
    • @KenWhite 谢谢。我可以看到所有三个选项的优点,我想这就是我未能将我的颜色钉在桅杆上的原因!
    猜你喜欢
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多