1

Я довольно новичок в PowerShell и в настоящее время не могу найти другого (лучше?) Решение по следующей проблеме:

Это мой код: (контрольный пример)

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Add-Item ([string]$fItem) {
    Write-Host "Do black magic with $fItem here"
}

$MyListOfItems=@()
$MyListOfItems+="foo"
$MyListOfItems+="bar"
$MyListOfItems+="foobar"
$MyListOfItems+="barfoo"
$MyListOfItems+="boofar"

$Form = New-Object System.Windows.Forms.Form 
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowAndShrink"
$Form.Add_Shown({$Form.Activate()})

$Counter=0
ForEach ($Item in $MyListOfItems) {
    $AddButton = New-Object System.Windows.Forms.Button
    $AddButton.Size = New-Object System.Drawing.Size(100,23)
    $AddButton.Location = New-Object System.Drawing.Point(10,(10+25*$Counter))
    $AddButton.Text = $Item
    #$Command = "Add-Item $Item"
    #$AddButton.Add_Click({ Invoke-Expression -command $Command }.GetNewClosure())
    $AddButton.Add_Click({ Add-Item $Item }.GetNewClosure())
    #$AddButton.Add_Click({ Add-Item $Item })
    $Form.Controls.Add($AddButton)
    $Counter++
}


[void]$Form.ShowDialog()

В PowerShell ISE он отлично работает:

Do black magic with foo here
Do black magic with bar here
Do black magic with foobar here
Do black magic with barfoo here
Do black magic with boofar here

С другой стороны, когда я запускаю его с Powershell.exe (читай: контекстное меню «Запуск с Powershell»), я получаю сообщение об ошибке на каждой из кнопок:

Add-Item : The term 'Add-Item' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Scripts\buttontest.ps1:62 char:24
+ $AddButton.Add_Click({ Add-Item $Item }.GetNewClosure())
+                        ~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Add-Item:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Если я изменю событие Add_Click для кнопки на:

$AddButton.Add_Click({ Add-Item $Item })

Я не получаю сообщение об ошибке, но прихожу к типичной ловушке - возвращать только последний элемент для каждой нажатой кнопки:

Do black magic with boofar here
Do black magic with boofar here
Do black magic with boofar here
Do black magic with boofar here
Do black magic with boofar here

Мне удалось заставить это работать, изменив область действия функции на глобальную область. Хотя это работает, это не устраивает меня. Модифицированная функция:

function global:Add-Item ([string]$fItem) {
    Write-Host "Do black magic with $fItem here"
}

Есть ли способ заставить этот код работать, избегая использования глобальной области для моей функции? Я искал несколько форумов и нашел много тем о различных способах реализации события add_click и различиях между Powershell ISE и командной строкой, но не смог найти ответ между ними

Примечание. Хотя я разрабатываю это в среде PowerShell 3, это все равно должно быть совместимо с PowerShell 2.

Заранее спасибо, Вим.

2 ответа2

2

Разница между ISE и «Запускать с Powershell» заключается в том, что ISE запускает сценарий в глобальной области, а «Запускать с Powershell» создает новую область действия для сценария. А код из других модулей (GetNewClosure создает новый модуль и привязывает к нему блок скрипта) может видеть глобальную область, но не может видеть вложенные области видимости. В качестве решения вы можете создать модуль самостоятельно и определить функцию Add-Item внутри этого модуля:

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$MyListOfItems=@(
    "foo"
    "bar"
    "foobar"
    "barfoo"
    "boofar"
)

$Form = New-Object System.Windows.Forms.Form 
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowAndShrink"
$Form.Add_Shown({$Form.Activate()})

$Counter=0
ForEach ($Item in $MyListOfItems) {
    $AddButton = New-Object System.Windows.Forms.Button
    $AddButton.Size = New-Object System.Drawing.Size(100,23)
    $AddButton.Location = New-Object System.Drawing.Point(10,(10+25*$Counter))
    $AddButton.Text = $Item
    $Module = New-Module {
        param($Item)
        function Add-Item ([string]$fItem) {
            Write-Host "Do black magic with $fItem here"
        }
    } $Item -Function @()
    $AddButton.Add_Click($Module.NewBoundScriptBlock({ Add-Item $Item }))
    $Form.Controls.Add($AddButton)
    $Counter++
}

[void]$Form.ShowDialog()

Вы также можете использовать другой подход и сохранить связанные данные в свойстве Tag элемента Control:

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Add-Item ([string]$fItem) {
    Write-Host "Do black magic with $fItem here"
}

$MyListOfItems=@(
    "foo"
    "bar"
    "foobar"
    "barfoo"
    "boofar"
)

$Form = New-Object System.Windows.Forms.Form 
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowAndShrink"
$Form.Add_Shown({$Form.Activate()})

$Counter=0
ForEach ($Item in $MyListOfItems) {
    $AddButton = New-Object System.Windows.Forms.Button
    $AddButton.Size = New-Object System.Drawing.Size(100,23)
    $AddButton.Location = New-Object System.Drawing.Point(10,(10+25*$Counter))
    $AddButton.Text = $Item
    $AddButton.Tag = @{Item = $Item}
    $AddButton.Add_Click({param($sender) Add-Item $sender.Tag.Item })
    $Form.Controls.Add($AddButton)
    $Counter++
}

[void]$Form.ShowDialog()
0

Я вставил ваш код в файл buttons.ps1 и запустил его с помощью powershell -File buttons.ps1 . Я получил новое окно формы со всеми кнопками, как и ожидалось. Я также попытался открыть файл с помощью "open with powershell", и это тоже сработало.

У меня нет пункта контекстного меню для "запуска с powershell" - но, очевидно, что контекстная запись выполняет ваш скрипт не так, как два выше. Можете ли вы попробовать выполнить свой скрипт из командной строки Windows?

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .