Несогласованный результат с функцией msgbox в консоли ISE и PowerShell

Я возился с объектом таймера и окнами сообщений и врезался в кирпичную стену.

Я пытаюсь управлять объектом таймера и всплывающими окнами с помощью функций многократного использования.

Я протестировал большинство этих функций по отдельности и сначала небольшими частями. Поскольку все работало, я решил связать все воедино.

Вместе это, похоже, не сработало, поэтому я разбил функциональность на основы:

# 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: action и action2. Итак, я решил, что проблема заключается в функции коробки, но:

  • если я вызову скрипт с ISE, он будет работать как положено
  • если я сначала выполню функциональный файл, а затем файл, он снова остановится/зависнет на (первом/ДаНет) msgBox
  • если я вручную введу функцию msgBox в консоль, а затем запущу файл, он отобразит msgBox, но не выполнит код в операторе else правильно (остановится после отображения (второго/OKonly)msgBox)

Я не могу придумать, как это имеет смысл, и я потратил много времени, пытаясь отладить/переключить его/заглушить и т. д.

edit1: Свежий день и немного дополнительной информации.

Я изменил функцию на windows.Forms, но не повезло. Затем я распечатал идентификаторы потоков для всех функций и частей скрипта и вуаля:

Main script
6
1176

myfunc
6
1176

Elapsedaction ($Thirtyaction)
11
4236

$action, который срабатывает при срабатывании Timerevent, запускается в новом пространстве выполнения 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"
    }
    }

person dopi    schedule 28.12.2015    source источник
comment
Привет, 1. сохранить сценарий в файле ps1 2. вызвать файл в консоли PS следующим образом: powershell -file "path/to/script.ps1" -sta 3. надеюсь, почему вы не используете WinForms или WPF? Это может быть проще и удалить исходную проблему.   -  person sodawillow    schedule 28.12.2015


Ответы (1)


Как интересно... Я провел несколько проверок.

Сначала я думал, что доступ к графическому интерфейсу будет просто заблокирован из окна консоли. Но я обнаружил, что окно сообщения отображается просто отлично, если вы вызываете его прямо (не из функции таймера) следующим образом:

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')
person Martin Maat    schedule 28.12.2015
comment
Твоя вторая мысль была настолько близка, насколько это возможно, и заставила меня задуматься. я поместил threadID во все функции и части скрипта, и оказалось, что Elapsed eventaction вызывается в другом потоке и, следовательно, не имеет доступа к ранее вызванным функциям... Я еще не уверен, как исправить/ обойти это, но, по крайней мере, я ближе к решению. Благодарю вас! - person dopi; 29.12.2015
comment
Он был озадачен вашим сообщением о том, что использование Windows.Forms не имело для вас значения (хотя было бы еще более странно, если бы оно имело место), потому что я тестировал его. Но похоже, что вызов все еще работает в ThirtyAction до вызова Show-MsgBox, но он не работает в Show-MsgBox, потому что PowerShell никогда не доходит до него! Вызов Show-MsgBox по какой-то причине не работает (во всяком случае, в моем окне консоли PowerShell). Я создал функцию-пустышку без параметров. Тот же результат, любой вызов функции из $ThirtyAction завершится ошибкой и, по-видимому, заблокирует сценарий. - person Martin Maat; 29.12.2015
comment
Да, это связано с тем, что блок сценария $thirtyaction запускается в новом пространстве выполнения PowerShell и, следовательно, больше не имеет доступа к этой функции. Обходной путь для этого состоит в том, чтобы снова создать файл функции в блоке сценария. нерешенная проблема заключается в том, что я не могу получить доступ к моему объекту таймера во время блока сценария. - person dopi; 29.12.2015
comment
О, теперь ты впереди меня. Я только что прочитал, что в наши дни их называют сессиями. Если вы еще не наткнулись на эту статью, она может вам помочь. learn-powershell.net /2013/04/19/ - person Martin Maat; 29.12.2015
comment
К сожалению, похоже, что у eventaction нет свойства runspace, и этот метод нельзя использовать :( - person dopi; 29.12.2015
comment
я нашел решение и обходной путь, если вы все еще заинтересованы. Я разместил это внизу вопроса. - person dopi; 29.12.2015
comment
Круто, я многому научился. Все еще странно, что я никогда не мог воспроизвести другой идентификатор потока в коде обработчика событий. Хотя симптомы были те же. Я тестировал на Winows 8 PS версии 4.0. - person Martin Maat; 29.12.2015