ISE 和 PowerShell 控制台中的 msgbox 函数结果不一致
Inconsistent result with msgbox function in ISE and PowerShell console
我在摆弄计时器对象和消息框时碰壁了。
我正在尝试通过可重用函数来管理计时器对象和弹出窗口。
我先单独测试了其中的大部分功能,并先测试了一小部分。由于一切正常,我决定将所有内容串在一起。
总的来说,它似乎不起作用,所以我将功能分解为基础:
# functionfile
function Show-MsgBox {
Write.Host "debugFUNC2"
#usage:
#Show-MsgBox -Prompt "This is the prompt" -Title "This Is The Title" -Icon Critical -BoxType YesNo -DefaultButton 1
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)] [string]$Prompt,
[Parameter(Position=1, Mandatory=$false)] [string]$Title ="",
[Parameter(Position=2, Mandatory=$false)] [string]$Icon ="Information",
[Parameter(Position=3, Mandatory=$false)] [string]$BoxType ="OkOnly",
[Parameter(Position=4, Mandatory=$false)] [int]$DefaultButton = 1
)
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
switch ($Icon) {
"Question" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Question }
"Critical" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Critical}
"Exclamation" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Exclamation}
"Information" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Information}
}
switch ($BoxType) {
"OKOnly" {$vb_box = [microsoft.visualbasic.msgboxstyle]::OKOnly}
"OKCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::OkCancel}
"AbortRetryIgnore" {$vb_box = [microsoft.visualbasic.msgboxstyle]::AbortRetryIgnore}
"YesNoCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::YesNoCancel}
"YesNo" {$vb_box = [microsoft.visualbasic.msgboxstyle]::YesNo}
"RetryCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::RetryCancel}
}
switch ($Defaultbutton) {
1 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton1}
2 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton2}
3 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton3}
}
$popuptype = $vb_icon -bor $vb_box -bor $vb_defaultbutton
$ans = [Microsoft.VisualBasic.Interaction]::MsgBox($prompt,$popuptype,$title)
return $ans
} #end function
function mafunc {
Write-Host "debugFUNC1"
$timer.start()
$timer
return 0
}
# calling file
$timer = New-Object System.Timers.Timer
#$timer.interval = 1800000
$timer.interval = 30000
$timer.Enabled = $true
$timer.AutoReset = $False
$scriptsleep = 0
$timer.stop()
$ThirtyAction = {
Write-Host "action"
Write-Host "action2"
if ((Show-MsgBox -Prompt "prompt" -Title "title" -Icon Critical -BoxType YesNo -DefaultButton 1) -eq "No") {
Write-Host "debugNO"
} else {
Write-Host "debugYES"
$timer
$timer.stop()
Show-MsgBox -Prompt "prompt" -Title "title" # standard promt = OKonly button
$timer.interval=30000
$timer.start()
$timer
Write-Host "timer start"
}
}
Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier ThirtySecTimer -Action $ThirtyAction
myfunc
我仍然无法 运行 脚本,因为它在 Elapsed 操作期间保持 "hanging" ($Thirtyaction
)。它在 PowerShell 控制台中显示的最后一件事是:action
和 action2
。所以我认为问题出在 box 函数中,但是:
- 如果我用 ISE 调用脚本,它会 运行 正如预期的那样
- 如果我先执行函数文件,然后再执行文件,它将再次 stop/hang 在 (first/YesNo) msgBox
- 如果我在控制台手动输入msgBox函数然后启动文件,它会显示msgBoxes,但不会正确执行
else
语句中的代码(在(second/OKonly)msgBox 已显示)
我想不出任何有意义的方式,我花了很多时间试图 debug/switch 它 up/dumb 它下来等等
edit1:新的一天和更多信息。
我将函数更改为 windows.Forms 但没有成功。
然后我开始为脚本的所有函数和部分打印出线程 ID,瞧:
Main script
6
1176
myfunc
6
1176
Elapsedaction ($Thirtyaction)
11
4236
Timerevent 触发时触发的 $action
在新的 PowerShell 运行空间中 运行,因此无法再访问之前定义的变量和函数。
我不确定如何解决或解决这个问题,但至少我知道问题出在哪里。欢迎任何想法,感谢已经回答并给我正确方向提示的人!
包含解决方案和解决方法的 Edit2:
感谢另一位用户如果找到了如何从 $Thirtyaction
脚本块中控制计时器属性的问题的答案:
我可以通过简单地将函数文件添加到脚本块中来使用 MsgBox 函数
我的变量解决方法是使用三个环境变量。我很确定这既不明智也不是最佳做法,但在我的特殊情况下不应该成为问题并且它有效。
作为示例,这里是我当前的 $Thirtyaction
脚本块:
$ThirtyAction={
. .\testfunction.ps1
$Sender.stop()
Write-Host "action"
Write-Host "action2"
if ((($ans=(Show-new-MsgBox -Prompt "Prompt" -Title "Title" -Icon "Warning" -BoxType "YesNo")) -eq "No") -or !($Env:Sessionsleepstate -eq 0)) {
Write-Host $Env:Sessionsleepstate
$Env:Sessionsleepstate = 3
if ($Env:Sessionsleepstate -eq 1) {
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
}
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
Write-Host "killfunction placeholder"
}
else{
$Sender.stop()
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
$Sender.interval=300000
$Env:Sessionsleepstate = 1
$Sender.start()
Write-Host "timer start"
}
}
多么有趣...我 运行 一些检查。
首先我认为 GUI 访问会被控制台阻止 window。但是我发现如果你像这样直接调用它(而不是从定时器函数),消息框显示得很好:
Add-Type -AssemblyName "Microsoft.VisualBasic"
[Microsoft.VisualBasic.Interaction]::MsgBox("MyPrompt",[microsoft.visualbasic.msgboxstyle]::OKOnly,"title")
然后我认为这可能是线程问题,在控制台中,计时器会在不同的线程上调用该函数,而 VisualBasic 程序集会忽略或拒绝它。但是我发现
Write-Host ([System.Threading.Thread]::CurrentThread).ManagedThreadId
Write-Host ([System.AppDomain]::GetCurrentThreadId())
无论是在主代码中还是在计时器调用的函数中都给出了相同的值。
然后我认为程序集可能无法再从计时器函数访问。但是我发现调用
([Microsoft.VisualBasic.Interaction]::Beep())
在控制台上工作得很好 window。
所以这很奇怪。但是,似乎有一个简单的解决方法:如果您使用 System.Windows.Forms.MessageBox,它会按预期工作:
Add-Type -AssemblyName System.Windows.Forms
$Result = [System.Windows.Forms.MessageBox]::Show('Now I''ll be a son of a gun...', 'How about that?', 'YesNo', 'Warning')
我在摆弄计时器对象和消息框时碰壁了。
我正在尝试通过可重用函数来管理计时器对象和弹出窗口。
我先单独测试了其中的大部分功能,并先测试了一小部分。由于一切正常,我决定将所有内容串在一起。
总的来说,它似乎不起作用,所以我将功能分解为基础:
# functionfile
function Show-MsgBox {
Write.Host "debugFUNC2"
#usage:
#Show-MsgBox -Prompt "This is the prompt" -Title "This Is The Title" -Icon Critical -BoxType YesNo -DefaultButton 1
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)] [string]$Prompt,
[Parameter(Position=1, Mandatory=$false)] [string]$Title ="",
[Parameter(Position=2, Mandatory=$false)] [string]$Icon ="Information",
[Parameter(Position=3, Mandatory=$false)] [string]$BoxType ="OkOnly",
[Parameter(Position=4, Mandatory=$false)] [int]$DefaultButton = 1
)
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
switch ($Icon) {
"Question" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Question }
"Critical" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Critical}
"Exclamation" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Exclamation}
"Information" {$vb_icon = [microsoft.visualbasic.msgboxstyle]::Information}
}
switch ($BoxType) {
"OKOnly" {$vb_box = [microsoft.visualbasic.msgboxstyle]::OKOnly}
"OKCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::OkCancel}
"AbortRetryIgnore" {$vb_box = [microsoft.visualbasic.msgboxstyle]::AbortRetryIgnore}
"YesNoCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::YesNoCancel}
"YesNo" {$vb_box = [microsoft.visualbasic.msgboxstyle]::YesNo}
"RetryCancel" {$vb_box = [microsoft.visualbasic.msgboxstyle]::RetryCancel}
}
switch ($Defaultbutton) {
1 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton1}
2 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton2}
3 {$vb_defaultbutton = [microsoft.visualbasic.msgboxstyle]::DefaultButton3}
}
$popuptype = $vb_icon -bor $vb_box -bor $vb_defaultbutton
$ans = [Microsoft.VisualBasic.Interaction]::MsgBox($prompt,$popuptype,$title)
return $ans
} #end function
function mafunc {
Write-Host "debugFUNC1"
$timer.start()
$timer
return 0
}
# calling file
$timer = New-Object System.Timers.Timer
#$timer.interval = 1800000
$timer.interval = 30000
$timer.Enabled = $true
$timer.AutoReset = $False
$scriptsleep = 0
$timer.stop()
$ThirtyAction = {
Write-Host "action"
Write-Host "action2"
if ((Show-MsgBox -Prompt "prompt" -Title "title" -Icon Critical -BoxType YesNo -DefaultButton 1) -eq "No") {
Write-Host "debugNO"
} else {
Write-Host "debugYES"
$timer
$timer.stop()
Show-MsgBox -Prompt "prompt" -Title "title" # standard promt = OKonly button
$timer.interval=30000
$timer.start()
$timer
Write-Host "timer start"
}
}
Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier ThirtySecTimer -Action $ThirtyAction
myfunc
我仍然无法 运行 脚本,因为它在 Elapsed 操作期间保持 "hanging" ($Thirtyaction
)。它在 PowerShell 控制台中显示的最后一件事是:action
和 action2
。所以我认为问题出在 box 函数中,但是:
- 如果我用 ISE 调用脚本,它会 运行 正如预期的那样
- 如果我先执行函数文件,然后再执行文件,它将再次 stop/hang 在 (first/YesNo) msgBox
- 如果我在控制台手动输入msgBox函数然后启动文件,它会显示msgBoxes,但不会正确执行
else
语句中的代码(在(second/OKonly)msgBox 已显示)
我想不出任何有意义的方式,我花了很多时间试图 debug/switch 它 up/dumb 它下来等等
edit1:新的一天和更多信息。
我将函数更改为 windows.Forms 但没有成功。 然后我开始为脚本的所有函数和部分打印出线程 ID,瞧:
Main script
6
1176
myfunc
6
1176
Elapsedaction ($Thirtyaction)
11
4236
Timerevent 触发时触发的 $action
在新的 PowerShell 运行空间中 运行,因此无法再访问之前定义的变量和函数。
我不确定如何解决或解决这个问题,但至少我知道问题出在哪里。欢迎任何想法,感谢已经回答并给我正确方向提示的人!
包含解决方案和解决方法的 Edit2:
感谢另一位用户如果找到了如何从 $Thirtyaction
脚本块中控制计时器属性的问题的答案:
我可以通过简单地将函数文件添加到脚本块中来使用 MsgBox 函数
我的变量解决方法是使用三个环境变量。我很确定这既不明智也不是最佳做法,但在我的特殊情况下不应该成为问题并且它有效。
作为示例,这里是我当前的 $Thirtyaction
脚本块:
$ThirtyAction={
. .\testfunction.ps1
$Sender.stop()
Write-Host "action"
Write-Host "action2"
if ((($ans=(Show-new-MsgBox -Prompt "Prompt" -Title "Title" -Icon "Warning" -BoxType "YesNo")) -eq "No") -or !($Env:Sessionsleepstate -eq 0)) {
Write-Host $Env:Sessionsleepstate
$Env:Sessionsleepstate = 3
if ($Env:Sessionsleepstate -eq 1) {
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
}
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
Write-Host "killfunction placeholder"
}
else{
$Sender.stop()
Show-new-MsgBox -Prompt "Prompt" -Title "Title"
$Sender.interval=300000
$Env:Sessionsleepstate = 1
$Sender.start()
Write-Host "timer start"
}
}
多么有趣...我 运行 一些检查。
首先我认为 GUI 访问会被控制台阻止 window。但是我发现如果你像这样直接调用它(而不是从定时器函数),消息框显示得很好:
Add-Type -AssemblyName "Microsoft.VisualBasic"
[Microsoft.VisualBasic.Interaction]::MsgBox("MyPrompt",[microsoft.visualbasic.msgboxstyle]::OKOnly,"title")
然后我认为这可能是线程问题,在控制台中,计时器会在不同的线程上调用该函数,而 VisualBasic 程序集会忽略或拒绝它。但是我发现
Write-Host ([System.Threading.Thread]::CurrentThread).ManagedThreadId
Write-Host ([System.AppDomain]::GetCurrentThreadId())
无论是在主代码中还是在计时器调用的函数中都给出了相同的值。
然后我认为程序集可能无法再从计时器函数访问。但是我发现调用
([Microsoft.VisualBasic.Interaction]::Beep())
在控制台上工作得很好 window。
所以这很奇怪。但是,似乎有一个简单的解决方法:如果您使用 System.Windows.Forms.MessageBox,它会按预期工作:
Add-Type -AssemblyName System.Windows.Forms
$Result = [System.Windows.Forms.MessageBox]::Show('Now I''ll be a son of a gun...', 'How about that?', 'YesNo', 'Warning')