現在、フォーム上に動的にコントロールを配置するようなアプリケーションを作成しているのですが、
以下の様な問題が発生しており、解決方法をお伺いしたいと思っています。
■問題点
当アプリケーションでは、主に以下の2つの機能を持っています。
1.あるフォーム上に、ユーザーが任意に、動的にボタン、エディットボックスなどのコントロールを追加
例えば、ボタンを追加した場合は、以下の様なソースで作成しています。
===================================================================
var
control : TControl;
.
begin
control := TButton.Create(nil);
control.Parent := TForm1;
control.Visible := True; .
.
===================================================================
また、追加後はジェネリックリスト(TList<TControl>)に
そのコントロールの参照を追加して、後でそのコントロールを参照できるようにしています。
2.上記1で追加したコントロールの論理削除(非表示)
⇛ジェネリックリストからコントロールを検索後、
コントロールの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です。
よろしくお願いいたします。
普通に考えると、生成、表示非表示を繰り返すだけで
オブジェクトのポインタがかぶってしまうことはないかと。
まずリストのデータの持ち方に問題があるのではないかと思います。
>上記を実現する単純な再現サンプルソース.........
>同じ手順でコントロールの追加、非表示を繰り返すプログラムでは、
> 同現象の再現ができませんでした。
と書いてありますからね.自力解決しかないでしょう.
⇛ http://shima.choco-ring.com/ESR/index_w_h.html かな ?
論理削除(非表示) 非表示は「論理削除」というんですか.知りませんでした.
>http://shima.choco-ring.com/ESR/index_w_h.html
すみません.
404 Not Found になってしまいますね.
コピペして,ブラウザのURL欄に入力すると表示するようです.
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;
>#上記を実現する単純な再現サンプルソースを作成しようとしたのですが、
> 同じ手順でコントロールの追加、非表示を繰り返すプログラムでは、
> 同現象の再現ができませんでした。
と言うことですから、それ以外の部分に問題があるのは確定でしょう・・・
> (実際はもっと複雑なプログラムのため、その作り方に原因がある可能性もあるのですが…)
> 一般的な解決方法があるのでしたらお伺いしたいと思っています。
実際のソースで、当面不要な部分をどんどんコメントアウトしつつ簡略化
していけば、問題にたどり着くと思いますけど?
皆様、ありがとうございます。
一般的なポインタの割り当て規則や制御方法があるならと
思ったのですが、そういうものはなさそうですね。
やはり、皆様が言われている通り、実際のソースで確認するしかないですよね。
いろいろと試してみます。ありがとうございました。
>一般的なポインタの割り当て規則や制御方法があるならと
>思ったのですが、そういうものはなさそうですね。
何でそういう方向に行くのか理解に苦しみますが,
「ポインタ」というのは,つまりポインタなんです.
今回の問題は,配列 (ジェネリックス) とその要素の単なる扱い方の問題だと思いますよ.
多分.おそらく.
[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
どうしても,メモリ(ポインタ)の割り当てや制御をしたければ,
コンパイラを自作することですね (^^;
Delphi で有名なところはこんなとこでしょうか.
[実践・Delphiでポインタ入門]
http://himagine.s20.xrea.com/delphi/pointer.htm
単純なバグっぽい気がします・・・。
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;
失礼
訂正します
誤
>if Assigned(list) then
>正
if Assigned(list[i]) then
ツイート | ![]() |