コントロールは親ウィンドウを持っていません、というエラーの対策方法

解決


かつ  2002-08-20 07:33:11  No: 1316

教えてください。困ってます。
カスタムコンポを作って、コードにGetDC(Handle);を入れるとフォームに配置する際、「コントロール・・・は親ウィンドウを持っていません」とメッセージが出てフォームにコントロールが配置されません。
GetDCをどこに書いても症状はおなじです。ただし、先にコンポを配置しておいて、あとからコンポのソースにGetDCを書くとちゃんと動作します。
それなりに色々試しましたがどうしようもありません。どうぞ教えてください。お願いします。


たかみちえ  URL  2002-08-20 19:42:44  No: 1317

よくありますね、それ。困ったものです。
CreateWndメソッドをオーバーライドして、そこでGetDCすると、わりとうまくいくようです。
ただ、Createと違って、必ず"最初にしか発生しない"というわけではないです。

  それから、ReleaseDCはDestroyWndに、ウィンドウを作り直す(メモ型コントロールのオプションを切り替えるときなど)必要があるときは、
RecreateWndを使います。


にしの  2002-08-21 06:31:27  No: 1318

どういうところでGetDCしていますか?
Paintをオーバーライドして、その中で使っていますか?

HandleAllocated関数で、ウィンドウハンドルが存在するか確認してからGetDCしてみるとか。
たいてい、うまく行かないのはロジックの間違いです。


かつ  2002-08-21 08:10:04  No: 1319

CreateWndをオーバーライドしましたがうまくいかず、functionでGetDCを使って必要最小限度にしましたが、それでもうまくいかず、いっそのことGetDCを外すとこれまたうまくいかず、また一からGetDCのないコードを書き直すとようやくうまくいきました。
それでロジックの間違い、ではたと気づきGetDCでエラーの出たコードをドンドンコメントアウトしていくと、if文で条件が2箇所あるところで出ていることがわかりました。具体的には
procedure TMyMemo.Change;
var
  i: Integer;
begin
  if (FMaxLines > 0) and (Lines.Count > MaxLines) then
    {ここに処理};
end;
{ここに処理}には何を書いてもエラーが出ます。
親ウィンドウを割り当てればそれでうまくいくのかなぁと思っていましたがそういうものでもないのでしょうか?(ちゃんとした割り当て方もわかっていませんが・・・)


たかみちえ  URL  2002-08-21 08:24:17  No: 1320

そういう系統のエラーは大体、エラーメッセージでおおよその見当はつくんですけど、
・コントロールXXXは親ウィンドウを持っていません。
  −原因
  親として参照しようとしているウィンドウは、まだ存在しない。
  −対処法
  parentプロパティに何か入れてみる
  できるのなら、ウィンドウハンドルの作成と同時に、inheritedのあとで処理を行う

・アプリケーション名のXXXXで、読み込み違反
  −原因
  参照しようとしたコンポーネントが、実はnil
  −対処法
  ちゃんとCreateが成功しているか、間違えてその前に開放してないか確認。
  nilのものにfreeを使おうとしてもエラーになるので、怪しいものはAssignedで調べてから。

  そのへんを調べてみてはどうでしょうか?
  それと、Changeイベントはどうやら、コントロールが実際に使えるようになる前にも、一度通っているようです。
そうだとすると、  もしかしたらLinesが使えないということもありえますね。

  ところで、
> CreateWndをオーバーライドしましたがうまくいかず
  とのことですけど、ちゃんとinheritedのあとでGetDCをしましたよね?
"あたりまえジャン!"だったら、悪いですけど。

  ところで
> procedure TMyMemo.Change;
  TMemoの拡張ですか?なら、これが参考になるかも。
http://www.geocities.co.jp/SiliconValley-PaloAlto/8945/tips/index2_14.html


にしの  2002-08-21 09:28:13  No: 1321

描画時に、デバイスコンテキストの取得・解放をするようにしてみてはどうですか?
> if (FMaxLines > 0) and (Lines.Count > MaxLines) then
ここでエラーが出たと言うことは、おそらくLines.Countが原因です。
Lines.CountのLinesは、TMemoStringsで、そのCreateがされていないのだと思います。
Createは、
constructor TCustomMemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Width := 185;
  Height := 89;
  AutoSize := False;
  FWordWrap := True;
  FWantReturns := True;
  FLines := TMemoStrings.Create;
  TMemoStrings(FLines).Memo := Self;
end;
と、TCustomMemoでしていて、Selfを入れているし、TMemoStrings.GetCountでは、
Memoに大してHandleAllocatedを調べ、存在すればメッセージを送っています。

カスタムコンポーネントのCreateは、ちゃんと
procedure TMyMemo.Create(AOwner: TComponent); override;
begin
  inherited Create(AOwner);

・・・から始まっていますか?
結構、inherited;と間違えてかく場合があります。引数を指定しないと、TObjectのCreateしか呼べません。


にしの  2002-08-21 09:29:31  No: 1322

失礼。

procedure TMyMemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
・・・でした。
override;は、宣言時に必要なだけです。実体にあるとエラーですね^^;


かつ  2002-08-22 05:00:31  No: 1323

色々なアドバイス本当にありがとうございます。
「親ウィンドウがない」ということなので親ウィンドウを割り当てようとしたことに無理があったみたいですね。
結局、エラーの出るところに
if Parent <> nil then
を入れるとうまく動きました。
なんとなく解決策としてはまずいような気もしているのですが・・・


たかみちえ  URL  2002-08-22 05:18:10  No: 1324

ファイルが見つからないとのことなので。
http://www.geocities.co.jp/SiliconValley-PaloAlto/8945/delphi/tip1.html
でも解決したのならいいかぁ。


かつ  2002-08-22 09:02:33  No: 1325

いえいえ、ファイルは見つかりました。そして参考にしています。
ただ、MyMemo=Class(TMemo)に
Canvas: TControlCanvas;
して、
constructor TMyMemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Canvas := TControlCanvas.Create;
end;

procedure TMyMemo.CreateWnd;
var
  DC: HDC;
begin
  inherited CreateWnd;
  DC := GetDC(Handle);  
  Canvas.Handle := GetDC(DC);
      {  処理  }
end;
やってみましたが、OSの再起動後にエラーが出たのです。今から思えば他のコードロジックに問題があったのかもしれませんが、現在は、
procedure TMyMemo.{ 処理名 };
var
  DC: HDC;
begin
  if Parent <> nil then
  begin
    DC := GetDC(Handle);
    Canvas.Handle := DC;
      { Canvasに対する描画処理 }
    Canvas.FreeHandle;
    ReleaseDC(Handle, DC);
  end;
end;
としています。
「解決」としましたが、解決したようなしてないような不安な気持ちはどうしてなんでしょう?
とにかく、色々勉強になりましたし、ありがとうございました。


にしの  2002-08-22 18:33:06  No: 1326

TControlCanvasは、文字通り「コントロールのキャンバス」です。
Canvas.Handleを使うのなら、TCanvasで十分です。
TControlCanvasを使うなら、Controlプロパティに自分自身を入れればOKです。
# もしかしてControlがnilだから親ウィンドウがないと言われていたり…?推測ですが。


かつ  2002-08-23 04:23:13  No: 1327

もともとはTCanvasだったのを、usesにGraphicsを書かなくてよい、という理由だけでTControlCanvasにしただけです。
結果は同じでしたので・・・


kazu  2002-08-23 08:03:13  No: 1328

すでに解決済になっていますが、
inherited Create(AOwner);
Parent  := AOwner as TWinControl;
FCanvas := TControlCanvas.Create; //←この1行

再現しましたのでその回避コードです。

#ちなみに、上記サイトのコードはDel3で作成したコードです。
#まさかDel6で動かないとは  (^^;;


kazu  2002-08-23 09:25:05  No: 1329

>inherited Create(AOwner);
>Parent  := AOwner as TWinControl;
>FCanvas := TControlCanvas.Create; //←この1行

間違えた(^^;

inherited Create(AOwner);
Parent  := AOwner as TWinControl;//←この1行を追加
FCanvas := TControlCanvas.Create;


snowman  2005-06-24 21:30:37  No: 1330

ずいぶん前の件ですが、うまくいった例としてPaintのオーバーライドを紹介します。

procedure   TMyCtrl.Paint;
begin
  inherited;
  case Tag of
    0: begin
      Child.Color := clCream;
      self.Font.Size := 12;
      Tag := 1;
    end;
  end;
end;

つまり、初めてのPaint呼び出し後には、親ウィンドウができていると言うことでしょうか?


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








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