none
Tee-Objectと($object=...)の処理の違い RRS feed

  • 質問

  • 下記のコードをファイルにして右クリックメニューから実行すると正常に動作するのですが、
    フォーム生成部の先頭部分を次行のコメントの内容に変えて同様に実行すると、ボタンをクリックした後で、
    「変数 '$form' は、設定されていないために取得できません。」の例外が発生します。
    因みにISEのスクリプトウィンドウにコピー&ペーストして実行した場合、後者だと何も起きません(フォームも閉じません)
    括弧だと後で意図が掴みづらいので、出来ればTee-Objectに記述を統一したいのですが、何が悪いのでしょうか?

    OSはXP Pro.(SP3)、.NET frameworkは1.0~3.5まで入っています。Microsoft Updateは大体7/10あたりの分まで適用済です。
    よろしくご教示お願いします。
    ※月に数度かつ1回30分程度しかネットにアクセスできませんので、環境に依存する部分はできるだけ仮定形で進めてもらえると有難いです。

    以下コード---------
    #requires -version 2.0
    Set-StrictMode -Version 2.0
    $ErrorActionPreference = 'Inquire'

    [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")|Out-Null

    New-Object -TypeName System.Windows.Forms.Button|Tee-Object -variable okbutton|ForEach-Object {
        $_.Size = New-Object -TypeName System.Drawing.Size(60, 20)
        $_.Location = New-Object -TypeName System.Drawing.Point(170, 200)
        $_.Text = "OK"
        $_.Add_Click({
            $form.Close()
        })
    }

    ($form = New-Object -TypeName System.Windows.Forms.Form)|ForEach-Object {
    #New-Object -TypeName System.Windows.Forms.Form|Tee-Object -variable form|ForEach-Object {
        $_.Controls.Add($okbutton)
        $_.Size = New-Object -TypeName System.Drawing.Size(300, 250)
        $_.Text = "条件を入力して下さい"
        $_.AcceptButton = $okbutton
        $_.showDialog()|Out-Null
    }
    $form.Dispose()

     

    2010年7月23日 2:01

回答

  • 以下のように括弧を付けるのはどうですか。

    (New-Object -TypeName System.Windows.Forms.Form|Tee-Object -variable form)|ForEach-Object {

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 4:59
  • まったくの勘違いでしたね。

    Tee-Object -variable NAME

    を実行すると、

    1. InputObject をすべて Pipeline に転送する
    2. 変数 NAME へ、転送したオブジェクトの複製をを格納する

    という順序で処理が実行されます。このため、

    $_.showdialog()

    に達した時点では、まだ For-Each の遅延評価によって、Tee-Object は 1 の最中です。つまり、まだ $form は作成されていません。$form が作成されていない状態で OK ボタンを押すので、$form が存在しないというエラーになっています。

    $form の作成は、Tee-Object のパイプライン処理が終わった後になりますので、対応としては

    1. For-Each を利用しない(Tee-Object の処理を完結させる)
    2. Tee-Object の遅延評価を無くす(すでに minminnana207 さんが書かれている方法)
    3. showdialog の実行を遅らせる

    の3つの方法があります。どの方法でも解決できると思いますが、複数のオブジェクトを処理するわけでもないのに For-Each を使用しているところが一番不自然なので、そこを直せるとベターだと思えますが、一番面倒そうですね。

    ちなみに、3番目の方法は、showDialog() を

    $form.ShowDialog() | Out-Null
    $form.Dispose()

    と、Dispose() の前に動かせば OK かと思います。

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 8:35
  • ついでに、オブジェクトのプロパティの初期化ですが、

    $okbutton = New-Object System.Windows.Forms.Button -Property @{
      Size = New-Object System.Drawing.Size(60, 20);
      Location = New-Object System.Drawing.Point(170, 200);
      Text = "OK";
      Add_Click = { $form.Close() };
    }
    

    とするほうが、見やすくてよいのではないかと思います。

     

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 8:47

すべての返信

  • 完全に想像ですが、(略)

    (なかなか勘違いなことを書いていたので、無用な混乱を避けるために削除しました)

    2010年7月23日 3:53
  • 以下のように括弧を付けるのはどうですか。

    (New-Object -TypeName System.Windows.Forms.Form|Tee-Object -variable form)|ForEach-Object {

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 4:59
  • まったくの勘違いでしたね。

    Tee-Object -variable NAME

    を実行すると、

    1. InputObject をすべて Pipeline に転送する
    2. 変数 NAME へ、転送したオブジェクトの複製をを格納する

    という順序で処理が実行されます。このため、

    $_.showdialog()

    に達した時点では、まだ For-Each の遅延評価によって、Tee-Object は 1 の最中です。つまり、まだ $form は作成されていません。$form が作成されていない状態で OK ボタンを押すので、$form が存在しないというエラーになっています。

    $form の作成は、Tee-Object のパイプライン処理が終わった後になりますので、対応としては

    1. For-Each を利用しない(Tee-Object の処理を完結させる)
    2. Tee-Object の遅延評価を無くす(すでに minminnana207 さんが書かれている方法)
    3. showdialog の実行を遅らせる

    の3つの方法があります。どの方法でも解決できると思いますが、複数のオブジェクトを処理するわけでもないのに For-Each を使用しているところが一番不自然なので、そこを直せるとベターだと思えますが、一番面倒そうですね。

    ちなみに、3番目の方法は、showDialog() を

    $form.ShowDialog() | Out-Null
    $form.Dispose()

    と、Dispose() の前に動かせば OK かと思います。

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 8:35
  • ついでに、オブジェクトのプロパティの初期化ですが、

    $okbutton = New-Object System.Windows.Forms.Button -Property @{
      Size = New-Object System.Drawing.Size(60, 20);
      Location = New-Object System.Drawing.Point(170, 200);
      Text = "OK";
      Add_Click = { $form.Close() };
    }
    

    とするほうが、見やすくてよいのではないかと思います。

     

    • 回答としてマーク HiroyS 2010年7月24日 0:45
    2010年7月23日 8:47
  • >以下のように括弧を付けるのはどうですか。

    括弧は…最近は個人的に使うコードしか書かなくて、色んな言語に浮気してるもんで、
    パッとみて意図が掴みにくい表現は出来れば減らしたいんですよね。トシなんで(笑)。
    というか、そもそもTee-Objectに変えたかった理由がそれなんです。後出しですみません。

    2010年7月24日 0:44
  • >ついでに、オブジェクトのプロパティの初期化ですが、

    おお、そんな方法があったのですね、不勉強で申し訳ないです。

    Powershellには変数宣言の強制オプションが(半端にしか)無いので、少しでも変数名を書く機会を減らしたかったのと

    もともとVB使いなんで、頭から「With互換のコード」しか浮かばなくって当初のコードだったんですが、そっちに変えたいと思います。ありがとうございます。

    2010年7月24日 0:54
  •  >For-Each を使用しているところが一番不自然なので

    仰るとおり不自然なのは承知してました…「意図がつかみにくい表現は避けるんじゃないのか?」←自分

    まあ別の返信にもあるとおり、頭っから「Withをどう移植するか」って考えしか浮かんでなかったです。

    まあ自分で使うコードなんで、全体の統一感優先で、各コントロールの生成・代入部分を纏めて頭に持ってきて、

    その後に各コントロールのプロパティの設定部分を置く事にします。…もちろんプロパティの初期化方法は直して。

    まあ問題ないと思いますが、結果はまた後日。有難うございました。

     

     

    2010年7月24日 1:12
  • ・・・って、オブジェクトの生成時しかプロパティ設定・メソッド実行の一括化って出来ないのですね。
    しかもcontrols.addとかはキーが被るとかで一回しか使えないし。
    当然半端はせずに全部追い出しましたが。

    これは後出しって言うよりはワザと問題を単純化してたのですが、実際には結構な数のコントロールが貼ってあったり。
    しかもラジオボタン入りのグループボックスとか。

    うう、変数名を書く回数がどんどん増えてる。 With か Option Explicit かどっちか欲しいです。

    ともあれ、今のところ無事に動いています。有難うございました。

    #今月は無理した(のは自分じゃないですが)ので次は来月の今頃まで見れません・・・

    2010年7月25日 0:38