PowerShell Memo

このサイトはPowerShell(MSH/Monad)奮闘記です

管理人「newpops吉岡洋」が
「PowerShell(旧名:MSH/Monad)」の研究結果を日々綴っていきます。

【お知らせ】
この日記からPowerShellのTipsを抽出し「PowerShell FAQ」として整理しました。


2006-12-31

[]Format-Listの謎

Format-Listを実行するとスレッドが変わる?

以下は、ボタンを1個乗せたフォームを表示するサンプルです。

当たり前ですが、普通に動作します。

リスト1

[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$button = New-Object Windows.Forms.Button
$form.Controls.Add($button)
$form.ShowDialog()

ところが、以下を実行すると、フォームにボタンを追加する行($form.Controls.Add($button))でエラーが発生します。

リスト2

[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$button = New-Object Windows.Forms.Button
$button | Format-List
$form.Controls.Add($button)
$form.ShowDialog()

リスト2で発生するエラー内容
"1" 個の引数を指定して "Add" を呼び出し中に例外が発生しました:
"あるスレッドで作成されたコントロールに対して、
別のスレッドのコントロールを親にすることはできません。"
発生場所 行:1 文字:19
+ $form.Controls.Add( <<<< $button)

リスト1とリスト2の違いは以下を実行したかどうかだけです。

$button | Format-List

Format-ListはButtonオブジェクトを破棄し、別のスレッドで再生成したのでしょうか?

エラーの理由が分かりません。

検証

フォームとボタン両方に対してFormat-Listを実行してみるとどうでしょうか?

リスト3

[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$form | Format-List
$button  = New-Object Windows.Forms.Button
$form.Controls.Add($button)     # エラー
$button | Format-List
$form.Controls.Add($button)     # 成功

フォームだけにFormat-Listを実行した場合はエラーが出ますが、ボタンにもFormat-Listを実行すると、エラーが発生しません。

仮説ですが、

Format-Listは自身専用のスレッドオブジェクトの整形を行う。

その際、対象オブジェクトを自身のスレッドで再生成する。

・・・という事なのでしょうか?


情報求む!

よこけんよこけん 2007/01/04 03:45 はじめまして、よこけんと申します。以前に私のブログの記事を紹介して頂きまして、ありがとうございます m(_ _)m

Format-Listによって作成元スレッドが変化するとは驚きです。
ちょっと気になったので調べてみました。 ( 長文すみません。 )


まず根源は、PowerShellではコマンドが実行されるスレッドが毎回異なることです。そのため、PowerShellではControlクラスを操作する際に特殊な処理を行っているようです。

具体的には、コントロールがハンドルと関連付けられる際、そのコントロールの作成元スレッドを偽って、スレッドID:0のスレッドが作成したことにしてしまいます。

作成元スレッドIDはControl.CreateThreadIdプロパティ ( 可視性:Internal ) にて取得できるのですが、コントロールがハンドルと関連付けられていない場合、このプロパティは現在のスレッドIDを返します。先に述べた通り、PowerShellではコマンドが毎回違うスレッドで実行されます。よって、ハンドルが生成されたコントロールとハンドルが生成されていないコントロールでは、作成元スレッドIDは必ず異なってしまいます。

Format-Listを呼び出すとコントロールがハンドルと関連付けられます。そのため、上記のような理由でエラーが発生してしまいます。


何故Format-Listを呼び出すとコントロールとハンドルが関連付けられるかまでは調べてません。恐らく、コントロールにハンドルが関連付けられていない状態でControl.Handleプロパティが呼び出されると、その時点でハンドルとの関連付けが行われてしまう ( CreateHandleメソッドが呼ばれてしまう ) という動作が関係していると思います。 ( あまり根拠はないので違うかも^^; )

当然ですが、Format-Listを呼び出している所を、$form.Handleとか$button.Handleに置き換えてみると全く同じ現象が発生します。


なお、ハンドルと関連付けられていないコントロールに対して、同じくハンドルが関連付けられていないコントロールを追加する場合 ( ↓の例のように。 ) は、一度のコマンドで実行されるために作成元スレッドIDが同一のものとなります。よってエラーは発生しません。
例: $form.Controls.Add($button);

newpopsnewpops 2007/01/05 02:42
よこけんさん、情報ありがとうございます。
かなり内部まで調査して下さったのですね。
私も検証したいのですが、上記は何のツール(デバッガ?)を利用したのですか?

よこけんよこけん 2007/01/05 12:33
私が使ったツールは、ReflectorとPowerShellです。ただ、ReflectorはほとんどControlクラス周辺を調べるためにしか使ってません。


すみません、一つ訂正させてください。
> まず根源は、PowerShellではコマンドが実行されるスレッドが毎回異なることです。そのため、PowerShellではControlクラスを操作する際に特殊な処理を行っているようです。
> 具体的には、コントロールがハンドルと関連付けられる際、そのコントロールの作成元スレッドを偽って、スレッドID:0のスレッドが作成したことにしてしまいます。

と書きましたが、勘違いでした。申し訳ないです…。
別にPowerShellが特殊なスレッド操作をしてるわけではなく、単にスレッドが既に終了している場合スレッドIDが0になるようです。
# ということで、それほど深く調査してはいなかったりします。


あと、Format-Listを呼び出すとハンドルが関連付けられるのはHandleプロパティが原因かも?といったことを書きましたが、Format-Listはプロパティとその値を列挙するのだから調べるまでもなくこれが原因でしょうね^^;朝方でボケてました。

よこけんよこけん 2007/01/05 12:56 度々申し訳ありません。肝心な検証方法について書いてなかったですね。
といっても、結局の所、PowerShellを使って、ControlクラスのCreateThreadIdプロパティやIsHandleCreatedプロパティなどを調べただけだったり。そこに行き着くまでには、Reflectorを使って色々と見渡して思考を巡らせてました。

newpopsnewpops 2007/01/06 04:33
ご享受ありがとうございます。
私も調べてみました。
確かに、Format-List前後でIsHandleCreatedの値が、
False→Trueになりますね。
Format-Tableだと、IsHandleCreatedはFalseのままなのですが、
パッと見、Format-ListとFormat-Tableでは、メイン処理部分は共通クラスのように見えます。
謎は深まるばかりです・・・。orz

newpopsnewpops 2007/01/06 16:23
>よこけんさん

さらに調べてみました。
間違っていたらご指摘下さい。

問題となるプロパティは以下の2つ。
 1.Handle
 2.AccessibilityObjectプロパティ

$buttonの1と2のどちらかのプロパティにアクセスすると、
$form.Controls.Add($button) でエラーが出ます。
#$buttonと$formの両方アクセスしているとAddは成功します。

1は、よこけんさの仰るように、
ハンドルが関連付けられていないControlのHandleプロパティにアクセスすると、
強制的にハンドルが作成されること、
2は、割り当てられているAccessibleObjectがない場合に、
ControlのAccessibleObjectプロパティにアクセスすると、
コントロールの新しいインスタンスが作成され、ハンドルが作成されること、
・・・が原因なのだと思います。

$form.Controls.Add($button)を実行すると、新規子スレッドが生成されますが、
コントロールが属するスレッドが同一でないと、
Add時に新規子スレッドが起こせないためエラーが発生するのかなと。

既定でFormat-List、Format-Tableがどのプロパティにアクセス&表示するかは、
XMLで定義されていますが、$buttonの場合、
既定(パラメータなし実行)のFormat-ListでHandleにアクセスし、
既定のFormat-TableはHandleにアクセスしません。
そのため、既定のFormat-Tableではエラーが発生しなかったようです。

試しに、Format-Listにパラメータを指定して、Handle以外のプロパティを表示すると、
Add時にエラーは発生しませんし、
試しに、Format-Tableにパラメータを指定して、Handleをを表示すると、
Add時にエラーは発生します。

よこけんよこけん 2007/01/06 21:46
> 2.AccessibilityObjectプロパティ

AccessibilityObjectプロパティもハンドルが作成される要因の一つだとは気づかなかったです。

> $form.Controls.Add($button)を実行すると、新規子スレッドが生成されますが、
> コントロールが属するスレッドが同一でないと、
> Add時に新規子スレッドが起こせないためエラーが発生するのかなと。

ちょっと違います。新規子スレッドが起こせないということではありません。
新規子スレッドを生成するのはPowerShellです。コマンドを実行するために毎回生成します。 ( 具体的にはSystem.Management.Automation.Runspaces.LocalPipelineクラスのStartPipelineExecutionメソッドが生成します。 )
一方、コントロールが属するスレッドが同一でないと例外が発生するのは、ControlCollection.Addメソッドの実装です。またAdd時にスレッドが起こされるということは元々ないです。

流れとしては以下のような感じです。
・PowerShellが新規子スレッド生成
・PowerShellが新規子スレッド開始
・新規子スレッド上で”$form.Controls.Add($button)”が実行される
・”$form.Controls.Add($button)”実行中に例外が発生
・新規子スレッド終了

newpopsnewpops 2007/01/06 22:34
>よこけんさん

> 流れとしては以下のような感じです。
> ・PowerShellが新規子スレッド生成
> ・PowerShellが新規子スレッド開始
> ・新規子スレッド上で”$form.Controls.Add($button)”が実行される
> ・”$form.Controls.Add($button)”実行中に例外が発生
> ・新規子スレッド終了

なるほど。よく分かりました。
私の勘違いですね。
ご教示、ありがとうございました。

もう1つ教えて頂けないでしょうか?

今回、良いやり方が分からず、
Spy++でAdd前後のスレッドの様子を見る、
という原始的な方法を取ったのですが、(^^;
誰がどこでスレッドを作っているのか調べるのに、
良いツールはありますか?
#机上デバッグという回答以外を希望・・。(^^;

よこけんよこけん 2007/01/07 00:14
うーん、残念ながら私はそういったツールは知らないです。 ( 特に探したことがないので、存在するかどうかはわかりません。 )
ご期待に沿えず申し訳ないですが、私の場合はやはりReflectorで逆コンパイルしたコードを机上デバッグというのが基本です ^^;
検証にはPowerShellが大変重宝しますね。今まではわざわざVisual Studioでコンソールアプリケーションプロジェクトを作成する必要がありましたから・・・ -_-;

newpopsnewpops 2007/01/07 02:17
>よこけんさん

>残念ながら私はそういったツールは知らないです。

了解です。
便利ツールを発見したら紹介しますね。

>検証にはPowerShellが大変重宝しますね。

それは本当に実感しますね。

調査がお手軽で良いです。
gm,fl,psbaseでたいてい調査可能ですし、
COMの特殊なオブジェクトでもpsobjectで対応できますし。
#Vistaに標準搭載してほしかったです・・・。orz

ekenkou_jp_seiryokuzai_31ekenkou_jp_seiryokuzai_31 2011/10/27 17:59 精力増強^-^アフリカ蟻

*☆*----*----*☆----☆*-----☆----*☆-----*☆*

[[精力剤]]、[[媚薬]]、健康美容商品の通販「e健康_JP」発毛、育毛剤http://www.ekenkou.jp/
精力剤[[アフリカ蟻]]African Ant http://www.ekenkou.jp/ca7/5/p-r7-s/
人気精力剤(seiryokuzai): [[香港版蟻力神(ホンコンバンイーリーシン)]]、[[蟻力神アリパワー]]、[[金偉哥]]、[[アフリカ蟻African Ant]]、[[好漢哥(コウカンカ)]]、[[中華牛宝]].
正規メーカーの精力剤をお届け致します。
精力剤[[蔵秘雄精]] http://www.ekenkou.jp/?ca=2

*☆*----*----*☆----☆*-----☆----*☆-----*☆*

kp100kp100 2012/07/19 11:40 ED、ぼっき不全の対応
ED(勃起不全・勃起障害)、精力減退、性欲減退とは
ED(勃起機能の低下)やその原因となる疾病(高血圧症、糖尿病、心臓病)、
および 精力剤、媚薬、漢方バイアグラに関するED薬を提供しております...。
メーカー直営、信用100%、プライバシー厳守!是非ご愛顧下さいませ !
漢方精力剤:http://kp100.com/category-178.html
女性用媚薬:http://kp100.com/category-196.html

biyakucombiyakucom 2013/04/23 11:47 無料媚薬ー媚薬コム
当店は海外の優れた媚薬を海外現地より日本の皆様へ直接お届けしております。
日本では手に入らない様々な品を格安の現地価格にてご提供します。
手数料・消費税一切無し!即日発送!
日本会社が運営していますのでご安心ください。外国人運営の店ではございません。
無料媚薬:http://www.biyaku.com/muryoubiyaku/
人気媚薬:モチベーター(Motivat)、LOSE CHASTITY (粉剤) 、
魔鬼天使Muira.PuamaII、German Black Widows、維高VIGAL。
媚薬コムhttp://www.biyaku.com/

biyakucombiyakucom 2013/04/25 18:17 お-元気ドツトコム
■バイアグラ、シアリス、レビトラの専門店「o-genki.com」
人気精力剤
(1)香港版蟻力神(ホンコンバンイーリーシン)
この蟻力神は、すでに製造中止となった「沈陽長港蟻宝」の蟻力神とは異なり、
製造メーカーの違う香港の蟻力神です。
http://www.o-genki.com/?product-313.html
(2)徳国黒金BlackGold
この精力剤は徳国(ドイツ)生まれの超強力勃起増強薬タブレットです。
http://www.o-genki.com/?product-316.html
威哥王(ウェイカワン)、強金龍キョウコンリュウ、蔵秘雄精(ゾウヒユウセイ)、
超能持久(チョウノウジキュウ)、巨人倍増、勃動力三体牛鞭、九州神龍。
http://www.o-genki.com/

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

トラックバック - http://d.hatena.ne.jp/newpops/20061231/p1