1

我正在摆弄计时器对象和消息框并撞到了一堵砖墙。

我正在尝试通过可重用的功能来管理计时器对象和弹出窗口。

我首先单独测试了这些功能中的大部分,并在小部分中进行了测试。由于一切正常,我决定把所有东西都串起来。

在一起,它似乎不起作用,所以我将功能分解为基础:

# 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 操作 ( $Thirtyaction) 期间一直“挂起”。它在 PowerShell 控制台中显示的最后一件事是:actionaction2. 所以我认为问题出在 box 函数中,但是:

  • 如果我用 ISE 调用脚本,它将按预期运行
  • 如果我先执行函数文件然后执行文件,它将再次在 (first/YesNo) msg​​Box 处停止/挂起
  • 如果我在控制台中手动输入 msgBox 函数然后启动文件,它会显示 msgBoxes,但不会else正确执行语句中的代码(在显示 (second/OKonly)msgBox 后停止)

我想不出任何有意义的方式,我花了很多时间尝试调试/将其向上切换/将其降低等。

编辑1:新的一天和更多的信息。

我将功能更改为 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"
    }
    }
4

1 回答 1

0

多么有趣……我做了一些检查。

首先,我认为 GUI 访问只会被控制台窗口阻止。但是我发现如果你直接调用它(而不是从定时器函数),消息框显示得很好,如下所示:

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())

从控制台窗口工作得很好。

所以这很奇怪。但是,似乎有一个简单的解决方法:如果您使用 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')
于 2015-12-28T23:10:48.260 回答