[英]Delphi (Seattle) - Closing a dynamically created modal form causes access violation
Summary: one form (Loan Form) dynamically creates a modal form called DatePickerForm (when user clicks a specific button). 简介:一种表单(贷款表单)可动态创建一个名为DatePickerForm的模式表单(当用户单击特定按钮时)。 After selecting a date in the DatePickerForm, the user clicks on that form's 'Close' button: (a BitBtn) - this is what causes an access violation error.
在DatePickerForm中选择一个日期后,用户单击该表单的“关闭”按钮:(一个BitBtn)-这是导致访问冲突错误的原因。
Details: 细节:
The purpose of the reusable modal DatePickerForm is to provide users with a consistent way of entering dates in special circumstances. 可重用的模式DatePickerForm的目的是为用户提供在特殊情况下输入日期的一致方法。 It will be used in multiple other situations - that is, if I get it to work as planned.
它会在其他多种情况下使用-也就是说,如果我能够按计划工作。
Exact error text is: "Project ABCD.exe raised exception class $C0000005 with message 'access violation at 0x0060d0b1: read of address 0x00000000'." 确切的错误文本是:“ Project ABCD.exe引发了异常类$ C0000005,消息为“在0x0060d0b1发生访问冲突:读取地址0x00000000”。
The code compiles and the program works fine until step 4 below: 代码会编译,并且程序可以正常运行,直到执行下面的第4步:
Run-time Process: 运行时过程:
My questions : 我的问题 :
A) Should this work or am I using dynamic form creation incorrectly? A)应该这样做还是我使用动态表单创建不正确?
B) Is there a better way to achieve this? B)是否有更好的方法来实现这一目标?
Any help will be appreciated. 任何帮助将不胜感激。
John 约翰
DatePickerForm code (complete): DatePickerForm代码(完整):
unit DatePicker_PopupForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ComCtrls;
type
TfmDatePicker_Popup = class(TForm)
DTDatePicker: TDateTimePicker;
lblDatePrompt: TLabel;
btnOK: TBitBtn;
procedure btnOKClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
fmDatePicker_Popup: TfmDatePicker_Popup;
implementation
{$R *.dfm}
procedure TfmDatePicker_Popup.btnOKClick(Sender: TObject);
begin
fmDatePicker_Popup.CloseModal;
end;
end.
Loan form - partial code (complete code is roughly 9700 lines long) 贷款形式-部分代码(完整代码长约9700行)
unit LoanForm;
interface
uses
Winapi.Windows, ......, DatePicker_PopupForm;
...
implementation
...
procedure TfmLoan.btnSetDefaultClick(Sender: TObject);
begin
DatePickerForm := TfmDatePicker_Popup.Create(Application);
DatePickerForm.DTDatePicker.Date := GD_ProcessDate;
DatePickerForm.ShowModal;
dDefaultDate := DatePickerForm.DTDatePicker.Date;
end;
...
end.
The documentation says: 该文件说:
Do not call CloseModal in your application.
不要在您的应用程序中调用CloseModal。 CloseModal is used by the VCL when a modal form needs to be closed.
当需要关闭模式窗体时,VCL使用CloseModal。 CloseModal does not close the form by itself;
CloseModal不会自行关闭表单; it simply calls the registered close events and updates the ModalResult property.
它仅调用已注册的关闭事件并更新ModalResult属性。
So, do as it says. 因此,按照说明进行操作。 Close a modal form by setting the form's
ModalResult
property. 通过设置窗体的
ModalResult
属性来关闭模式窗体。
The easiest way to do that is to remove the button OnClick
event handler. 最简单的方法是删除按钮
OnClick
事件处理程序。 Instead set the button's ModalResult
property in the designer. 而是在设计器中设置按钮的
ModalResult
属性。
It is clear from the error message that you are accessing a nil
pointer. 从错误消息中很明显,您正在访问
nil
指针。 And the reason for that is because you are calling CloseModal()
(which you should not be calling directly in the first place) on a global fmDatePicker_Popup
object pointer that is not actually pointing at a valid Form object to begin with: 这样做的原因是因为您正在全局
fmDatePicker_Popup
对象指针上调用CloseModal()
(首先不应直接调用它),而该指针实际上并未指向有效的Form对象开头:
procedure TfmDatePicker_Popup.btnOKClick(Sender: TObject);
begin
fmDatePicker_Popup.CloseModal; // <-- fmDatePicker_Popup is not assigned!
end;
The reason fmDatePicker_Popup
is nil
is because in btnSetDefaultClick()
, when you create your TfmDatePicker_Popup
object, you are assigning it to a different DatePickerForm
variable instead of the fmDatePicker_Popup
variable: fmDatePicker_Popup
为nil
的原因是,在btnSetDefaultClick()
,当您创建TfmDatePicker_Popup
对象时,您将其分配给另一个DatePickerForm
变量而不是fmDatePicker_Popup
变量:
procedure TfmLoan.btnSetDefaultClick(Sender: TObject);
begin
DatePickerForm := TfmDatePicker_Popup.Create(Application); // <--
...
end;
TfmDatePicker_Popup
shouldn't be relying on any external pointers to itself at all. TfmDatePicker_Popup
根本不应该依赖任何指向其自身的外部指针。 Since btnOKClick()
is a member of the TfmDatePicker_Popup
class, it should be using the implicit Self
pointer instead: 由于
btnOKClick()
是TfmDatePicker_Popup
类的成员,因此应改为使用隐式的Self
指针:
procedure TfmDatePicker_Popup.btnOKClick(Sender: TObject);
begin
Self.CloseModal;
end;
Or simply: 或者简单地:
procedure TfmDatePicker_Popup.btnOKClick(Sender: TObject);
begin
CloseModal;
end;
That being said, CloseModal()
is the wrong thing to call anyway. 话虽如此,
CloseModal()
仍然是错误的调用。 It doesn't actually close the Form, it just triggers the Form's OnClose
event. 它实际上并没有关闭Form,它只是触发Form的
OnClose
事件。 Per the ShowModal()
documentation: 根据
ShowModal()
文档:
To close a modal form, set its
ModalResult
property to a nonzero value .要关闭模式形式, 请将其
ModalResult
属性设置为非零值 。
ShowModal()
internally calls CloseModal()
when it detects the ModalResult
has become non-zero. 当检测到
ModalResult
已变为非零时, ShowModal()
内部调用CloseModal()
。 If the OnClose
event handler sets its Action
parameter to caNone
, the ModalResult
is reset to 0 and the Form is not closed. 如果
OnClose
事件处理程序将其Action
参数设置为caNone
,则ModalResult
将重置为0,并且不会关闭Form。
So use the Form's ModalResult
property instead, like the documentation says to: 因此,请改用Form的
ModalResult
属性,如文档所述:
procedure TfmDatePicker_Popup.btnOKClick(Sender: TObject);
begin
Self.ModalResult := mrOk;
end;
Which can then be automated by removing the OnClick
handler altogether and instead setting the button's ModalResult
property to a non-zero value (or, in the case of TBitBtn
, set its Kind
property, which also sets its ModalResult
). 然后可以通过完全删除
OnClick
处理程序并将按钮的ModalResult
属性设置为非零值来自动执行此操作(或者,对于TBitBtn
,请设置其Kind
属性,该属性也将设置其ModalResult
)。 When a button on a modal Form is clicked, it assigns its own ModalResult
to its parent Form's ModalResult
before triggering its OnClick
event. 单击模式窗体上的按钮时,它将在触发其
OnClick
事件之前将其自己的ModalResult
分配给其父窗体的ModalResult
。
And then, you should also change btnSetDefaultClick()
to look more like this instead: 然后,您还应该更改
btnSetDefaultClick()
使其更像这样:
procedure TfmLoan.btnSetDefaultClick(Sender: TObject);
var
DatePickerForm: TfmDatePicker_Popup;
begin
DatePickerForm := TfmDatePicker_Popup.Create(nil);
try
DatePickerForm.DTDatePicker.Date := GD_ProcessDate;
if DatePickerForm.ShowModal = mrOk then
dDefaultDate := DatePickerForm.DTDatePicker.Date;
finally
DatePickerForm.Free;
end;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.