当我尝试初始化新窗体时,地址处的 Delphi 访问冲突

unhi4e5o  于 2023-05-28  发布在  其他
关注(0)|答案(2)|浏览(153)

当我尝试打开新的子mdi窗体时,我想检查该子窗体是否已经打开。我尝试用Assignet方法来实现这一点。当我使用Assigned时,我得到错误Acccess violation at address。此错误说明内存中没有示例。

uses SubjectUnit;

procedure TMainForm.SubjectClick(Sender: TObject);
var
  Frm : TSubjectForm;
begin
  if not Assigned(TSubjectForm) then
  begin
    Frm := TSubjectForm.Create(Self);
    Frm.Show;
  end
  else
    Frm.WindowState := TWindowState.wsMaximized;
end;

我知道什么是问题,但什么是好的方法初始化Frm?在if语句之前?如果我初始化它,我将在if块中重复初始化

2guxujil

2guxujil1#

你不能传递一个类类型给Assigned(),你必须传递一个指向对象示例的指针。Assigned()只会告诉你指针是否是nil,而不是它是否指向一个有效的对象。
因此,除非您保存指向MDI子窗体对象的指针,否则Assigned()无论如何都不会真正帮助您。
根据您的需要,您可能需要通过主窗体的MDIChildren[]属性循环查看每个现有的子窗体,以查看它是否与您正在寻找的内容(即类类型,窗口标题等)匹配。
例如,如果你只需要TSubjectForm的一个示例,那么你需要这样做:

uses
  ..., SubjectUnit;

procedure TMainForm.SubjectClick(Sender: TObject);
var
  Frm : TSubjectForm;
begin
  Frm := nil;

  for i := 0 to MDIChildCount-1 do
  begin
    if MDIChildren[i] is TSubjectForm then
    begin
      Frm := TSubjectForm(MDIChildren[i]);
      Break;
    end;
  end;

  if not Assigned(Frm) then
  begin
    Frm := TSubjectForm.Create(Self);
    Frm.WindowState := TWindowState.wsMaximized;
  end;

  Frm.Show;
end;

或者,你可以使用子窗体的全局指针来完成同样的事情,例如:

uses
  ..., SubjectUnit;

procedure TMainForm.SubjectClick(Sender: TObject);
begin
  if not Assigned(SubjectForm) then
  begin
    SubjectForm := TSubjectForm.Create(Self);
    SubjectForm.WindowState := TWindowState.wsMaximized;
  end;

  SubjectForm.Show;
end;
var
  SubjectForm: TSubjectForm;

...

procedure TSubjectForm.FormDestroy(Sender: TObject);
begin
  SubjectForm := nil;
end;
o4tp2gmn

o4tp2gmn2#

代码中有几个问题。
首先,不能在类上调用Assigned。您需要在对象引用Assigned(Frm)上调用它。
其次,非托管类型的局部变量(包括对象引用)不会自动初始化为nil,而是包含随机数据。在这样的未初始化变量上调用Assigned是未定义的行为,它将给予随机结果。

procedure TMainForm.SubjectClick(Sender: TObject);
var
  Frm : TSubjectForm;
begin
  // Frm is not initialized at this point and 
  // following check can randomly pass or fail
  if not Assigned(Frm) then

如果你有一些代码逻辑,需要检查是否有一些局部引用被赋值,你需要首先将该引用初始化为nil。

procedure TMainForm.SubjectClick(Sender: TObject);
var
  Frm : TSubjectForm;
begin
  Frm := nil;
  ...
  // now we can safely check whether reference points to valid object 
  // or not
  if not Assigned(Frm) then

然而,为了实现你想要的功能,上面的代码将是无用的,因为局部变量将总是为空,这样的代码将总是创建新的表单示例,你可能最终会有多个主题表单。
从你的代码中,似乎你需要有一个单一的主题形式,然后你需要声明变量,将持有该形式在更广泛的上下文中作为字段在TMainForm类或作为全局变量,而不是在本地的方法。

type
  TMainForm = class(...
  ...
    fSubjectFrm: TSubjectForm;
  end;

procedure TMainForm.SubjectClick(Sender: TObject);
begin
  if not Assigned(fSubjectFrm) then
  begin
    fSubjectFrm := TSubjectForm.Create(Self);
    fSubjectFrm.Show;
  end
  else
    fSubjectFrm.WindowState := TWindowState.wsMaximized;
end;

一旦创建,只要其所有者对象(在本例中为主窗体)存在,这样的主体窗体示例就将存在。

相关问题