自動生成していないFormからの参照のエラーを回避するには?

解決


Jun2013  2015-05-07 00:21:12  No: 47271

いつもお世話になります。

XE7.1のwin32でテストしていて、
「モジュール'xxx.exe'のアドレス  xxxxxxでアドレスyyyyyに対する
読み込み違反がおきました」
となります。

Formが3コあり、メインはForm1で自動生成がForm1とForm3です。
Form1からForm2を呼び出して、Form3のコンポーネントを参照しています。
Form2の1度目の呼び出しからのForm3のコンポーネント参照ではエラーが出ません。
2度目からの呼び出しでの参照では上記エラーとなります。

元々Form2も自動作成でした。Form2が呼ばれたときと閉じたときの変更内容を
取得したくてForm2の自動生成をやめました。

以下がForm1での記述です。
procedure TForm1.FormCreate(Sender: TObject);
begin
  Form2   :=  nil;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Form2) then
    Form2 := TForm2.Create(Application);
  Form2.Show;
end;

以下がForm2での記述です。
procedure TForm2.ButtonListDispClick(Sender: TObject);
var
  i   : integer;
  item  : TListboxItem;
begin
  with Form3 do begin
      //リストをクリア
      SelectList.Clear;    //ここでエラーになる
      SelectList.BeginUpdate;
      〜したい処理  省略〜

  end;  //with
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action  := TCloseAction.caFree;
  Form2   := nil;
end;

ご教授いただけませんでしょうか。
宜しくお願いいたします。


igy  2015-05-07 00:47:56  No: 47272

SelectList は、呼び出す前には、どのような状態ですか?
(Freeされたり、してませんか?)


Jun2013  2015-05-07 03:24:46  No: 47273

早速の返答ありがとうございます。

SelectListはFreeとかしていないです。
テストで、Form3にボタンを貼り付けてそのボタンのTextを
参照するだけでもこのエラーは発生します。

XE8でもXE7.1VCLでも同様の結果となります。


igy  2015-05-07 05:27:39  No: 47274

挙げられたコードのうち、

>procedure TForm2.ButtonListDispClick(Sender: TObject);

の処理を

>Form3にボタンを貼り付けてそのボタンのTextを参照するだけ

に置き換えたものを、試したところ、こちらでは、エラーは出ませんでした。(Delphi XE8)


Jun2013  2015-05-07 07:22:59  No: 47275

igyさん  試していただいてありがとうございます。

こちらではエラーになります。
長文となりますが、UPします。問題解決のために行数を少なくしたもので
業務としては意味のないものです。

まずはプロジェクト*************************************************
program z1156;
uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2},
  Unit3 in 'Unit3.pas' {Form3};
{$R *.res}
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm3, Form3);
  Application.Run;
end.

次にUNIT1のType以降************************************************
type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;
var
  Form1: TForm1;
implementation
{$R *.fmx}
uses Unit2;
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Form2) then
    Form2 := TForm2.Create(Application);
  Form2.Show;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  Form2   :=  nil;
end;
end.

次にUNIT2のType以降************************************************
type
  TForm2 = class(TForm)
    Button1: TButton;
    ButtonListDisp: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ButtonListDispClick(Sender: TObject);
  private
    { private 宣言 }
    procedure ButtonSettingOkClick(Sender: TObject);
  public
    { public 宣言 }
  end;
var
  Form2: TForm2;
implementation
{$R *.fmx}
uses Unit1, Unit3;
procedure TForm2.Button1Click(Sender: TObject);
begin
  close;
end;
procedure TForm2.ButtonListDispClick(Sender: TObject);
begin
  Label1.Text   :=  Form3.SelectOkButton.Text;  //ここでエラー
  Form3.Panel1.Parent   :=  Form2;
  Form3.Panel1.Visible  :=  true;
  Form3.SelectOkButton.OnClick      :=  ButtonSettingOkClick;
end;
procedure TForm2.ButtonSettingOkClick(Sender: TObject);
begin
  Form3.Panel1.Visible    :=  false;
  self.Label1.text   :=  Form3.SelectOkButton.Text;  //これは参照のテスト用
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action  := TCloseAction.caFree;
  Form2   := nil;
end;
end.

最後にUNIT2のType以降************************************************
FormにPanelを配置しその上にListBoxとLabelとButtonを置いています。
type
  TForm3 = class(TForm)
    SelectList: TListBox;
    Panel1: TPanel;
    SelectOkButton: TButton;
    SelectTitle: TLabel;
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;
var
  Form3: TForm3;
implementation
{$R *.fmx}
end.

こちらでしている操作は、
(1)プログラムを立ち上げて、
(2)Button1をクリックしてForm2を表示
(3)Form2のButtonListDispをクリックして、Form3にあるパネルを表示
(4)SelectOkButtonを押下
(5)Form2の右上の閉じるボタン[X]を押下
(6)Form1に戻るので(2)の操作
(7)(3)の操作をするとエラーが発生
となります。

Form2を自動生成にして、Form2のFormCloseをコメント化すると
エラーは発生しません。
//  Action  := TCloseAction.caFree;
//  Form2   := nil;
不思議です。


igy  2015-05-07 07:51:36  No: 47276

>に置き換えたものを、試したところ、こちらでは、エラーは出ませんでした。(Delphi XE8)

ちなみに、これは、Delphi XE8 VCL 32bit版 でした。
FMX 32bit版 でも試したところ、こちらもエラーがでませんでした。

OS:Windows 7 64bit


通りすがり  2015-05-07 08:16:28  No: 47277

エラーが出る行にブレークポイントを設定して、その行にあるもの、つまり
Form3、Form3.SelectOkButton、Form3.SelectOkButton.Text、Label1
といったものに何が入っているかを確認してみてはいかがでしょう?


tor  2015-05-07 08:18:35  No: 47278

TForm2.ButtonListDispClick の中にある
> Form3.Panel1.Parent   :=  Form2;
これを外しても結果は同じですか?
このままForm2が解放されると、Form3.Panel1がParent不在のまま取り残されることになってまずそうですが……

ちなみに、Form内で別のまとまったコンポーネントを再利用したいとかの用途だったら、Frameを使った方がいい気がします。


tor  2015-05-07 08:26:18  No: 47279

と言うか、Parentを変更したから、Form2が解放される時にForm3も一緒に解放されていますねこれは。
やっぱり、こういう用途だったらFrameにするべきでしょう……。


Jun2013  2015-05-07 17:56:36  No: 47280

igyさん
何か環境が違うのでしょうか。XE7.1,XE8ともにWindows8の環境でWin32,Win64
でエラーが発生します。Android5.0.1でも同様です。

通りすがりさん
エラーの箇所にブレークポイントを設定してみると、Form3.SelectOkがnilでした。

torさん
>これを外しても結果は同じですか?
パネルが表示されなくなります。

それで原因と思われることがわかりました。Parentの扱いに考慮が足りていなかったのではないかと。
ご指摘のとおりForm2が解放されたときその子(?)となってしまっているPanelも
なくなるのではないかと。Form2が再生成されたときはPanelは生成されないと。

そこでButtonSettingOkClickに
      Panel1.Parent   :=  Form3;
として、Parentを戻してみたところエラーは発生せず期待した動きになりました。

コンポーネントを増やしたくないためにこういうことをしているのですが、
避けたほうがいいのでしょうか。Frameを使ったことがないのですが勉強
して試してみます。


Jun2013  2015-05-07 22:14:49  No: 47281

igyさん

>>に置き換えたものを、試したところ、こちらでは、エラーは出ませんでした。>(Delphi XE8)

>ちなみに、これは、Delphi XE8 VCL 32bit版 でした。
>FMX 32bit版 でも試したところ、こちらもエラーがでませんでした。

>OS:Windows 7 64bit

最初の私の情報提供不足からこの結果になったと思います。
申し訳ありませんでした。


Jun2013  2015-05-08 20:31:04  No: 47282

ありがとうございました。
解決です。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加