delphi – 将自动完成字符串分配给的访问冲突
我正在使用自动完成功能修改编辑控件:
Auto append/complete from text file to an edit box delphi 我想从DB加载自动完成字符串.我在自动完成控件后代上声明了新属性: 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).定义了值,而不是那时的垃圾(例如,我可以在调试器中查看字符串列表). ‘FStrings’是一个空的字符串列表. 当控件放在窗体上时,它工作正常.但是如果我将它放在用户输入DBGridEH单元格时显示的自定义inplace编辑器中,则不会. inplace编辑器是这样的: 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. 这个inplace编辑器继承自这样的抽象类: unit UMyInplaceEditor; interface uses Windows,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控件时抛出异常. 是什么导致访问违规? 解决方法
问题是您使用的代码错误处理接口引用计数.以下是相关摘录:
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对象的接口. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |