释放表单时访问冲突

时间:2019-02-18 14:09:09

标签: delphi

我正在使用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.

1 个答案:

答案 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;