質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

1回答

2206閲覧

メインフォーム 内のコントロールのイベント を 補助フォーム で利用したい (イベントを好きな場所で発生させたい)

kamikazelight

総合スコア305

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2019/03/25 06:36

編集2019/03/25 07:55

前提・実現したいこと

メインフォーム単体で機能が成り立っているところに
入力補助のために補助フォームを作りたいと思ったのですが

メインフォームだけで機能としては成り立っているので
出来ればメインフォームでモジュールには補助フォームを管理したくありません。

自分なりに考えて
指定イベントの発生時に自身のインスタンスに保存されているスクリプトブロック
を実行する
クラスEventHook を作ってみたのですが
EventHookRun メソッドを実行したときに
事前に渡したスクリプトブロック内の$This
EventHook がps1に書かれているときは
正常に EventHook のインスタンス になるのですが
psm1に書かれているときは
イベントの発生源のオブジェクト
になってしまい EventHook のプロパティにアクセスできません...

どうしたらモジュールにしても正常に実行できるようになるのでしょうか?
教えて頂きたいです。

別の方法でも構いません。
お願い致します。

該当のソースコード

powershell

1# EventHook.psm1 2 3Using NameSpace System.Collections.Generic 4Using NameSpace System.Windows.Forms 5Using NameSpace System.Drawing 6Using NameSpace System.Collections.Specialized 7 8 9<# 10 柔軟なイベント実行を補助します。 11#> 12class EventHook 13{ 14 static [OrderedDictionary] $EventHooks = [OrderedDictionary]::new() 15 [string] $Guid 16 [Object] $Hook 17 [scriptblock] $RunBlock 18 [Object] $Argument 19 20 # コンストラクタの共通の処理です。 21 hidden [void] Common ([Object] $HookObject, [String] $MethodName, [ScriptBlock] $Block) 22 { 23 $This.Guid = New-Guid 24 [EventHook]::EventHooks[$This.Guid] = $This 25 $This.Hook = $HookObject 26 $This.RunBlock = $Block 27 28 [scriptblock] $EventBlock = [scriptblock]::Create('[{0}]::SearchRun("{1}", $args)' -f @($This.Gettype().Name,$This.Guid)) 29 $HookObject.$MethodName($EventBlock) 30 } 31 32 # コンストラクタ 33 EventHook ([Object] $HookObject, [String] $MethodName, [ScriptBlock] $Block) 34 { 35 $This.Common([Object] $HookObject, [String] $MethodName, [ScriptBlock] $Block) 36 } 37 38 # コンストラクタ 39 EventHook ([Object] $HookObject, [String] $MethodName, [ScriptBlock] $Block, [Object] $Set_Argument) 40 { 41 $This.Common([Object] $HookObject, [String] $MethodName, [ScriptBlock] $Block) 42 $This.Argument = $Set_Argument 43 } 44 45 # 保存されているスクリプトブロックの実行 46 [Void] Run ([Object[]] $Set_Argument) 47 { 48 Write-Host "Run : $($This.GetType().name)" 49 if ($Set_Argument.Count -gt 0) 50 { 51 $This.RunBlock.Invoke($Set_Argument) 52 } 53 else 54 { 55 $This.RunBlock.Invoke() 56 } 57 } 58 59 # GUIDが一致するインスタンスを探してRunを実行する 60 static [Void] SearchRun ([String] $GUID, [Object[]] $Set_Argument) 61 { 62 [EventHook] $Obj = [EventHook]::EventHooks[$GUID] 63 64 if ($obj -ne $null) 65 { 66 $obj.Run($Set_Argument) 67 } 68 69 } 70 71}

powershell

1# テスト.ps1 2 3Using NameSpace System 4Using NameSpace System.Collections.Generic 5Using NameSpace System.Windows.Forms 6Using NameSpace System.Collections.Specialized 7 8 9Using Module .\EventHook.psm1 10 11Add-type -AssemblyName System.WIndows.Forms 12 13 14$Form = [Form]::new() 15 16$text = [TextBox]::new() 17 18$Form.Controls.Add($text) 19 20[EventHook]::New($Form,"Add_Click",{ 21 22<# 23 EventHook が $Thisになってほしいのに 24 EventHookがpsm1 に書かれていると 25 イベント発生源の オブジェクトが $Thisになる 26#> 27 Write-Host "Block : $($This.Gettype().name)" 28}) 29 30$Form.showDialog()

試したこと

Run メソッド 内で スクリプトブロックを実行する前に
GetNewClosure()したりしてみましたがだめでした。

補足情報(FW/ツールのバージョンなど)

powershell v5
Win 10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

EventHook の実装から、
「イベントの処理の中で$this$_以外の任意の情報を使って処理できるようにしたい(汎用的に)」
ということだと解釈しました。

詳しく検証していませんが、以下の方法はどうでしょうか?

1. 「イベント登録時に」 GetNewClosure() を行う

GetNewClosure()は呼び出した瞬間の変数を束縛するため、スクリプトブロック実行前に呼び出してもあまり意味が無いはずです。

参考

powershellのクロージャを理解する - Qiita

powershell

1Using NameSpace System.Windows.Forms 2Add-Type -AssemblyName System.Windows.Forms 3 4$Form = [Form]::new() 5$text = [TextBox]::new() 6 7$Form.Controls.Add($text) 8 9& { 10 $abc = "abc" 11 $Form.add_Click({ 12 13 $abc | Out-Host 14 15 }.GetNewClosure()) 16} 17$null -eq $abc # => $true $abc という変数は存在しない。 18 19$Form.ShowDialog() 20# => クリックすると abc が出力される

2. Register-ObjectEvent を使う

EventHookと似たような機構のものとして Register-ObjectEvent があります。

代わりにこちらを使ってみるのはどうでしょうか?

powershell

1Using NameSpace System.Windows.Forms 2 3Add-Type -AssemblyName System.WIndows.Forms 4 5 6$Form = [Form]::new() 7$text = [TextBox]::new() 8 9$Form.Controls.Add($text) 10 11# Register-ObjectEvent の引数が多いので、スプラッティングで指定する。 12[hashtable]$regEventArgs = @{ 13 # イベントソースのオブジェクト及びイベント名。 14 InputObject = $Form 15 EventName = 'Click' 16 17 # $true にするとコマンドレットの返り値を無くし、Get-EventSubscriber や Unregister-Event からイベントを隠蔽する。 18 # $true にしたときはGet-EventSubscriber -Force | Unregister-Event -Force など -Force スイッチをつけることで取得・削除が可能。 19 SupportEvent = $false 20 21 # 任意の情報を指定。 22 MessageData = @('任意の引数', [datetime]::Now) 23 Action = { 24 Write-Host "Block : $($sender.Gettype().Name)" 25 26 # 任意の情報の確認。 27 $event.MessageData | Out-Host 28 29 # Register-ObjectEvent の Action 内専用の自動変数。 30 # $event, $eventSubscriber, $sender, $eventArgs, $args 31 # $sender は $this 相当。 32 # $eventArgs は $_ 相当。 33 # $args は $this, $_ 相当。 34 35 # 自動変数の中身確認用。 36 Get-Variable -Name event, eventSubscriber <#, sender, eventArgs, args#> | 37 ForEach-Object -Process { 38 $_.Name 39 $_.Value 40 } #| Out-Host 41 42 } 43} 44 45Register-ObjectEvent @regEventArgs 46 47$Form.ShowDialog()

投稿2019/03/25 12:07

編集2019/03/26 12:37
imihito

総合スコア2166

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2019/03/25 14:14

1. ですが 同じサイトを参考にしていました。 使い方はあっているように感じるのですが... $Argument に好きなオブジェクトを入れておいて あとでスクリプトブロックから使うために ブロック内の $This が EventHook になるようにしたかったので [Void] Run ([Object[]] $Set_Argument) { Write-Host "Run : $($This.GetType().name)" $Closure = $This.RunBlock.GetNewClosure() if ($Set_Argument.Count -gt 0) { $Closure.Invoke($Set_Argument) } else { $Closure.Invoke() } } としたのですが Write-Host "Run : $($This.GetType().name)" は確かに EventHook になっている(期待通り) のですが $Closure 内の Write-Host "Block : $($This.Gettype().name)" が Form になってしまうのです。 Thisが EventHook の環境下 で .GetNewClosure() しているのに ダメなのです..... 更によくわからないのが EventHook をpsm1 にしないで ps1(同じスクリプト)に書くと Write-Host "Block : $($This.Gettype().name)" の結果が正しく EventHook になるのです.... 2.は 確認で MessageData = @('任意の引数', [datetime]::Now, $text) Action = { Write-host $event.MessageData[2].text } とここだけ書き換えて テキストボックスの文字を一文字ずつ増やしながらクリックしてみたのですがフォームが閉じた後にまとめてイベントが発生してしまうのですが こちらは私の使い方が間違っているのでしょうか イベントは都度発生させたいです。 お願い致します。
kamikazelight

2019/03/26 01:03

Register-ObjectEvent を別スレッドで非同期実行すればいけるかと思い試してみたのですが なぜか 一回目は正常に動作するのですが 二回目以降が イベントが発生しなくなってしまいました...
kamikazelight

2019/03/26 01:29 編集

非同期実行は 過去に作ったやつを使いました。 https://sites.google.com/view/kmkzyozora/Home/Program/PowerShell/ReuseScript/MultiThread_psm1 終了してしまうことが問題なのかと思い ループで終わらないようにしてみたのですが 結果は同じでした... $RunSpace = [MultiThread]::new() [ThreadInfo] $Thread = $RunSpace.Create({ param($regEventArgs) Register-ObjectEvent @regEventArgs while ($True){ Start-Sleep -Milliseconds 25 Write-host $(@(Get-EventSubscriber).Count -ne 0) } },@{regEventArgs = $regEventArgs}) $Form.ShowDialog() $RunSpace.Dispose()
kamikazelight

2019/03/26 01:43

理由はよくわかりませんが Action内の Get-Variable -Name event, eventSubscriber <#, sender, eventArgs, args#> | ForEach-Object -Process { $_.Name $_.Value } #| Out-Host を削除したところ正常に動作しました。 これでやりたいことが出来そうです。 ありがとうございました。
imihito

2019/03/26 12:41

すみません、Register-ObjectEvent は自分のコードにミスがあったのも要因かもしれません(# が全角)。
kamikazelight

2019/03/27 00:45

なるほど #| Out-Host のところでしょうか? 現在 提示頂いているコードなら単純に非同期にするだけで 動作確認出来ました。 全角 # があってもエラー起きずにこういったことが起きる場合もあるのですね... ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問