コントロール動的生成時に同じポインタが割り当てられないようにするには?


Delphi3年目  2013-02-19 04:02:58  No: 43888

現在、フォーム上に動的にコントロールを配置するようなアプリケーションを作成しているのですが、
以下の様な問題が発生しており、解決方法をお伺いしたいと思っています。

■問題点
  当アプリケーションでは、主に以下の2つの機能を持っています。
  1.あるフォーム上に、ユーザーが任意に、動的にボタン、エディットボックスなどのコントロールを追加

  例えば、ボタンを追加した場合は、以下の様なソースで作成しています。
  ===================================================================
  var
    control : TControl;
    .  
  begin
    control := TButton.Create(nil);
    control.Parent := TForm1;
    control.Visible := True;  .  
    .  
  ===================================================================

  また、追加後はジェネリックリスト(TList<TControl>)に
  そのコントロールの参照を追加して、後でそのコントロールを参照できるようにしています。

  2.上記1で追加したコントロールの論理削除(非表示)
      &#8667;ジェネリックリストからコントロールを検索後、
      コントロールのVisibleプロパティをFalseにします。
      (非表示にするのみとなり、後で再表示もできるようにしています。)

  しかしながら、上記の1と2の操作を繰り返すうちに、
  「1のコントロール動的生成時(Createメソッド)のタイミングで、  2で論理削除済みのコントロールと同じポインタが割り当てられてしまう」と
  いう現象が発生いたしました。

  このとき、2のコントロールは、同じポインタで上書きされてしまうため、参照不可となってしまいます。
  ※1と2はまったく違う種類のコントロール(例えば1がパネルで2がボタン)の場合でも発生してしまいます。
  ※コントロール数は10個以下でも発生いたします。

  2は表示上は消えていますが、VisibleをFalseにしているだけで
  解放(Free)などはしていないため、同じポインタが割り当てられてしまう
  原因が不明な状態となっています。

■質問点
  ①一般的に、フォーム上に動的にコントロールの追加及び、非表示(VisibleをFalse)を繰り返すことにより、
    同じポインタが重複して割り当てられるのは一般的なものなのでしょうか?
    (Delphi内部で、重複してポインタを割り当てられないように制御はされないものなのでしょうか?)
  
  ②また、当問題に関する回避方法はあるのでしょうか?
    (例えば、Create時に割り当てるポインタの制御がプログラム側でできれば解決できると思っているのですが、
      VCLソースのCreateのメソッドなどを追ってもそれらしき箇所は見当たりませんでした。。。)

#上記を実現する単純な再現サンプルソースを作成しようとしたのですが、
  同じ手順でコントロールの追加、非表示を繰り返すプログラムでは、
  同現象の再現ができませんでした。
  (実際はもっと複雑なプログラムのため、その作り方に原因がある可能性もあるのですが…)
  一般的な解決方法があるのでしたらお伺いしたいと思っています。

なお、DelphiのバージョンはXE2です。
よろしくお願いいたします。


  2013-02-19 10:06:31  No: 43889

普通に考えると、生成、表示非表示を繰り返すだけで
オブジェクトのポインタがかぶってしまうことはないかと。
まずリストのデータの持ち方に問題があるのではないかと思います。


Mr.XRAY  2013-02-19 16:17:57  No: 43890

>上記を実現する単純な再現サンプルソース.........
>同じ手順でコントロールの追加、非表示を繰り返すプログラムでは、
>  同現象の再現ができませんでした。

と書いてありますからね.自力解決しかないでしょう.

&#8667   http://shima.choco-ring.com/ESR/index_w_h.html かな ?
論理削除(非表示)  非表示は「論理削除」というんですか.知りませんでした.


Mr.XRAY  2013-02-19 16:22:06  No: 43891

>http://shima.choco-ring.com/ESR/index_w_h.html

すみません.
404 Not Found になってしまいますね.
コピペして,ブラウザのURL欄に入力すると表示するようです.


take  2013-02-19 18:03:46  No: 43892

TListでどのように管理しているかも掲載した方が
解決に繋がりそうですね

こんな感じかしら?

【追加】
control := TButton.Create(nil);
control.Parent := TForm1;
control.Visible := True;
List.Add(control);

【論理削除】
for i := 0 to List.Count-1 begin
  if List.Items[i] = Value then begin
    TComponent(List.Items[i]).Visible := False;
    break;
  end;
end;


Sara  2013-02-19 18:18:11  No: 43893

>#上記を実現する単純な再現サンプルソースを作成しようとしたのですが、
>  同じ手順でコントロールの追加、非表示を繰り返すプログラムでは、
>  同現象の再現ができませんでした。
と言うことですから、それ以外の部分に問題があるのは確定でしょう・・・

>  (実際はもっと複雑なプログラムのため、その作り方に原因がある可能性もあるのですが…)
>  一般的な解決方法があるのでしたらお伺いしたいと思っています。

実際のソースで、当面不要な部分をどんどんコメントアウトしつつ簡略化
していけば、問題にたどり着くと思いますけど?


Delphi3年目  2013-02-20 00:38:17  No: 43894

皆様、ありがとうございます。
一般的なポインタの割り当て規則や制御方法があるならと
思ったのですが、そういうものはなさそうですね。

やはり、皆様が言われている通り、実際のソースで確認するしかないですよね。
いろいろと試してみます。ありがとうございました。


Mr.XRAY  2013-02-20 08:19:01  No: 43895

>一般的なポインタの割り当て規則や制御方法があるならと
>思ったのですが、そういうものはなさそうですね。

何でそういう方向に行くのか理解に苦しみますが,
「ポインタ」というのは,つまりポインタなんです.
今回の問題は,配列 (ジェネリックス) とその要素の単なる扱い方の問題だと思いますよ.
多分.おそらく.

[10−1.変数とポインタ]
http://www9.plala.or.jp/sgwr-t/c/sec10.html

[4 変数へのアクセス]
http://www-watt.mech.eng.osaka-u.ac.jp/~tasai/ptrdoc/node4.html

どうしても,メモリ(ポインタ)の割り当てや制御をしたければ,
コンパイラを自作することですね (^^;


Mr.XRAY  2013-02-20 08:31:46  No: 43896

Delphi で有名なところはこんなとこでしょうか.

[実践・Delphiでポインタ入門]
http://himagine.s20.xrea.com/delphi/pointer.htm

本家
http://docwiki.embarcadero.com/RADStudio/XE3/ja/%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%A8%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%9E%8B


通りすがり  2013-03-03 05:43:05  No: 43897

単純なバグっぽい気がします・・・。

List に追加したコントロールは削除できていますか?
削除できて無ければ、たとえば

for i:=0 to list.Count-1 do
begin
  if Assigned(list) then
  begin
    みたいなコードは list[i] が解放済みでも
    ここを通ります。
  end;
end;

list.Remove(MyControl)

等でMyControl.Freeと合わせて
きちんと削除してやらなければなりません。

>※1と2はまったく違う種類のコントロール(例えば1がパネルで2がボタン)の場合でも発生してしまいます。
var

  ctr: TControl;
begin
  if Assigned(ctr) then
  begin
   if ctr is TPanel then
   begin
   end else
   if ctr is TButton then
   begin
   end
  end
end;

のようにクラスで分岐可能です。

他に Tag プロパティが自由に使えるので識別にも使えます。

グローバルに
var MyControlCount:integer=0 

とでもしておいて

ctr.Tag:= MyControlCount;
inc( MyControlCount)

と、作る度に増やしていけば
Tag が被ることは無いでしょう。

Tag が被っていたり EAccessViolationとか出るようなら
実は開放されてないとか、開放されたけどリストから消えてなかったとか
そういうことになりますね。

ctrl を常に動的に作ってるなら
その ctrl を管理する親クラスを TPersisten や TList から派生して

function TMyControls.Add: TControl;
begin
  Result:= TMyControl.Create(nil)
  inherited Add(Result);
end;

のように追加とか削除とか書き換えとかをTMyControls以外から
できないようにしてしまえばデバッグも楽かと思います。

規模にもよりますが・・・。
いろんなクラスのインスタンスを一括で監視するとか、煩雑になってしまいそうなら TListではだんだん対応しづらくなってくるしコードも毎回クラスチェックしたりとかしないと行けないと思うんで派生してしまったほうがいい気がします。

あとは、イベントハンドラに Sender: TObject があるなら、それを有効活用しましょう。

procedure TForm1.ControlClick(Sender:TObject)
begin
  if not Assigned(Sender) then Exit;
  if Sender is TButton then
  begin
     TButton(Sender).Visible:=False;
  end else
  if Sender is TPanel then
  begin
     TPanel(Sender).Visible:= False;
  end;
end;


通りすがり  2013-03-03 05:44:37  No: 43898

失礼
訂正します


>if Assigned(list) then

>正
if Assigned(list[i]) then


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

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






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