我正在使用Devart控件备份数据库。一切运行良好,直到我尝试释放包含备份组件(TDump)和进度条的表单。我将调用移到了代码的最后部分,并在尝试释放它之前检查了是否已分配
procedure TfrmMain.CmdBackupExecute(Sender: TObject);
var
SaveDialog: TSaveDialog;
QRYString: String;
frmBackup: TfrmBackup;
Password: String;
BackupPassword: String;
MasterPassword: String;
CurrentFrame: TFrameType;
OldUser: String;
OldPassword: String;
begin
dmVintage.tblSettings.Open;
BackupPassword:= dmVintage.tblSettings.FieldByName('BackupRestorePWord').AsString;
MasterPassword:= dmVintage.tblSettings.FieldByName('MasterPWord').AsString;
InputPassword('Enter Backup Password', Password);
if Password = BackupPassword then
try
try
//close current frame and change to root
CurrentFrame:= FrameManager.CurrentFrameType;
FrameManager.Clear;
dmVintage.connMain.LoginPrompt:= False;
OldUser:= dmVintage.connMain.Username;
OldPassword:= dmVintage.connMain.Password;
dmVintage.connMain.Connected:= False;
dmVintage.connMain.Username:= 'root';
dmVintage.connMain.Password:= MasterPassword;
dmVintage.connMain.Connect;
SaveDialog:= TsaveDialog.Create(frmMain);
SaveDialog.Filter := 'SQL file|*.sql';
SaveDialog.DefaultExt:= '.sql';
SaveDialog.FileName:= 'VintageData';
if SaveDialog.Execute then
begin
frmBackup:= TfrmBackup.Create(frmMain);
frmBackup.Show;
frmBackup.mdVintage.BackupToFile(AddTimestampToFilename(SaveDialog.FileName), QryString);
//FreeAndNil(frmBackup);
//FreeAndNil(SaveDialog);
dlgI('Backup Seccessful');
end;
Except on E: Exception do
dlgW2('TfrmMain.CmdBackupExecute', E.Message);
end;
finally
//ShowMessage('Finally');
if Assigned(frmBackup) then
FreeAndNil(frmBackup);
if Assigned(SaveDialog) then
FreeAndNil(SaveDialog);
//reset connection and load old frame
dmVintage.connMain.Connected:= False;
dmVintage.connMain.Username:= OldUser;
dmVintage.connMain.Password:= OldPassword;
dmVintage.connMain.Connect;
dmVintage.connMain.LoginPrompt:= True;
FrameManager.LoadFrame(CurrentFrame);
end
else
dlgE('Invalid Backup Password');
end;
function TfrmMain.AddTimestampToFilename(Value: String): String;
var
Extension: String;
FileName: String;
FormattedDataTime: String;
begin
Extension:= ExtractFileExt(Value);
FileName:= ChangeFileExt(Value, '');
DateTimeToString(FormattedDataTime, 'yyyymmdd_hhmm', Now);
FileName:= FileName + '_' + FormattedDataTime;
Result:= ChangeFileExt(FileName, Extension);
end;
“备份”表单非常简单,其中带有一些TDump组件标签和一个进度条。
unit uBackup;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uDataVintage, DADump, MyDump,
Vcl.ComCtrls, Vcl.StdCtrls;
type
TfrmBackup = class(TForm)
mdVintage: TMyDump;
lblBackingUpTable: TLabel;
lblTable: TLabel;
Label3: TLabel;
pbBackup: TProgressBar;
procedure mdVintageBackupProgress(Sender: TObject; ObjectName: string;
ObjectNum, ObjectCount, Percent: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
procedure TfrmBackup.mdVintageBackupProgress(Sender: TObject;
ObjectName: string; ObjectNum, ObjectCount, Percent: Integer);
begin
Application.ProcessMessages;
if lblTable.Caption <> ObjectName then
lblTable.Caption:= ObjectName;
pbBackup.Position:= Percent;
end;
end.
答案 0 :(得分:1)
在释放(尤其是非模式的)表单时使用frmBackup.Release
。
释放表单
该表单未以模态方式显示,您也无法显示,因为您是从主表单控制它的,因此无法在模态表单上使用。
我认为您会遇到访问冲突,因为您“直截了当地”释放了表单,而表单本身仍然仍然可见并正在处理消息。因此,即使您的实例已被清理,表单代码(通用的TForm代码)在某些时候仍可能会尝试对表单执行某些操作。即使在代码中调用“ Close”,也会遇到此问题,因为关闭也不是一个同步过程,并且需要表单来处理消息。
通常,解决方案是调用frmBackup.Release
而不是frmBackup.Free
。这样,表单便为其自身排队了一条消息。它将首先处理它必须执行的其他工作,并在某个时刻遇到此消息并开始正常的清理过程,然后最终释放自己。通常,这是从表单本身上的按钮单击事件关闭表单的方法,但我认为它也会使您摆脱这种泡菜。
关于Free和FreeAndNil的一般提示
在大多数情况下,您不需要调用FreeAndNil,尤其是不需要在您确切知道何时为其分配值的局部变量上。 FreeAndNil唯一要做的就是将您的引用设置为nil,对于以后超出三行范围的变量,根本不需要。
根本不需要在致电if assigned
甚至是FreeAndNil
之前先致电Free
。 Assigned仅检查引用是否为nil,这也是Free在内部所做的。没错:这是不会引发错误的有效代码:
var
o: TObject;
begin
o := nil;
o.Free;