gpt4 book ai didi

delphi - 将自动完成字符串分配给的访问冲突

转载 作者:行者123 更新时间:2023-12-03 15:10:11 26 4
gpt4 key购买 nike

我正在从这里修改具有自动完成功能的编辑控件: 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 的 setter 是:

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 控件时才会引发异常。

什么原因导致访问冲突?

最佳答案

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

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

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

然后代码如下:

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

所以我们将保存对对象而不是接口(interface)的引用。这看起来已经很可疑了。

然后我们这样做:

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

destructor TAutoCompleteEdit.Destroy;
begin
FACList := nil;
inherited;
end;

这里没有任何东西可以让对象保持事件状态。在代码的其他地方,我们引用了 IEnumString 接口(interface)。但是,一旦该引用被释放,该对象就会认为没有剩余的引用。所以它被删除了。然后,稍后,代码引用 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 引用。让引用计数像第一个解决方案一样管理生命周期。但随后您需要实现另一个接口(interface),允许 TAutoCompleteEdit 获取 TStrings 对象。

关于delphi - 将自动完成字符串分配给的访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28064712/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com