スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Control.InvokeとIsHandleCreated

http://nekokan333.blog.fc2.com/blog-entry-359.htmlの続き。なかなかにハマりました。

control.Invokeする際にInvokeRequiredプロパティを参照することはよくありますが、コントロールがハンドルに関連付けられていない場合、InvokeRequiedプロパティがfalseを返してしまうことが知られています(結果的にInvokeできないでスレッドセーフにアクセスできない)。詳しくは例えばこれ↓
http://igeta.cocolog-nifty.com/blog/2007/06/invoke.html

ならInvokeRequiredの前に

if(!control.IsHandleCreated) return;

みたいな文をつけちゃえばいいかというと、だいたいはこれでOKなんですが、たまにハマることがあります。自分がハマった例を紹介します。

ドッキングウィンドウのアプリ(要はほっぽアルファ)を作ってるときに、処理の軽量化のために表示していないウィンドウを初期化時にインスタンスを確保しないように変更してみたのですが、そもそも表示していないときの処理を全く実装していないので最初のIsHandlerCreatedの罠に陥ります。ハンドルが関連付けられるタイミングはよくわからないのですが、フォームだったらShowしちゃえばおそらく関連付けられるはず! ということで、Showしてから初期化の処理を行うことにしました。

ここでハマりポイント1つ目。次のような初期化メソッドがあったとします

public class SomeUserControl : UserControl
{
// : : :
//初期化メソッド
public void Init()
{
if(!this.IsHandleCreated) return;
//初期化処理 …
}
}


フォームにはこのユーザーコントロール(SomeUserControl)を配置するとします。フォームをShowしているのに(正確にはDockPanelSuiteのdockPanel.LoadFromXmlでドッキングウィンドウを読み込んでいるのでライブラリの環境依存の問題かもしれない)、直後にInit()内のthis.IsHandleCreatedはなんとfalseを返してしまいます。どうも親フォームの関連付けタイミングと、格納されているコントロールの関連付けタイミングは違うのでは…?という推測ができます(詳しくはよくわからないからプロの人おしえて)。

この例のthis.IsHandleCreatedの部分は次のように直すとちゃんとtrueを返します

if(this.FindForm().IsHandleCreated) return;

何らかの理由でフォームに直接格納されないコントロールもあることもふまえて、次のように書くのが安全かと思います。

if(!this.IsHadleCreated)
{
var form = this.FindForm();
if(form == null) return;
if(!form.IsHandleCreated) return;
}


つづいてハマりポイントその2。末端のInvokeする静的メソッドにも同様に、
×:

delegate void SetLabelTextCallBack(Label label, string text);
public static void SetLabelText(Label label, string text)
{
if(label.InvokeRequired)
{
SetLabelTextCallBack d = new SetLabelTextCallBack(SetLabelText);
label.Invoke(d, new object[]{ label, text};
}
else
{
label.Text = text;
}
}

というメソッドがあったとして、これもIsHandleCreatedの罠にハマってしまうので、
修正案1

delegate void SetLabelTextCallBack(Label label, string text);
public static void SetLabelText(Label label, string text)
{
if(!this.IsHadleCreated)
{
var form = this.FindForm();
if(form == null) return;
if(!form.IsHandleCreated) return;
}
if(label.InvokeRequired)
{
SetLabelTextCallBack d = new SetLabelTextCallBack(SetLabelText);
label.Invoke(d, new object[]{ label, text};
}
else
{
label.Text = text;
}
}

としてみます。格納されているコントロールのハンドルは関連付けられていないのに、フォームが関連付けられていることをたよりに、関連付けされていないコントロールをInvokeしてやろうというぱっと見すごく怪しいコードです

DockPanel Suiteを使わないでこの条件を再現しようと思ったらなんか上手く行かなかったんでちゃんと検証できてないけど、Control.Invokeする際に(ハンドルの関連付けがされていれば)、フォームをInvokeしてもそのフォームに格納されているコントロールをInvokeしてもちゃんと動くことを踏まえると、フォーム側が関連付けられていればたとえコントロールが関連付けられていなくてもInvokeRequiredはfalseを返さないのでは?という推測。もしかしたら、偶然にもInit()の部分の呼び出しが全てコントロールと同じスレッドになっていて奇跡的にInvokeしなくても例外を吐いていないだけなのかもしれない。 にしてもなんか気持ち悪い話だなと思いました(小並感)
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。