1

Second question today but this time I believe there's something really strange going on. The following code is not my original, I tried to reproduce the behavior with as few lines as possible. In my original code I'm importing the application paths and names with a config file and call the CreateButton function in a foreach loop.

I'm trying to create multiple buttons with a function wich should open different applications. When I hardcode the Path everything is working. when I use variables in a foreach loop every button uses the LAST path submitted. Here is code that works:

# Define Form
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(260,250)
$Form.Text = "ToolBox"
$form.StartPosition = "CenterScreen"

# Create Button Function
function CreateButton ($ButtonCommand, $ButtonName, $ButtonLocX, $ButtonLocY, $ButtonSizeX, $ButtonSizeY)
{
$btn = New-Object System.Windows.Forms.Button
$btn.add_click($ButtonCommand)
$btn.Text = $ButtonName
$btn.Location = New-Object System.Drawing.Size($ButtonLocX,$ButtonLocY)
$btn.Size = New-Object System.Drawing.Size($ButtonSizeX,$ButtonSizeY)
$form.Controls.Add($btn)
}

$Command={Start-Process -FilePath "C:\Windows\system32\mmc.exe"}
$Text='MMC'
$LocationX=20
$LocationY=10
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Command={Start-Process -FilePath "C:\Windows\regedit.exe"}
$Text='Regedit'
$LocationX=20
$LocationY=70
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Command={Start-Process -FilePath "C:\Windows\System32\cmd.exe"}
$Text='CMD'
$LocationX=20
$LocationY=100
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

#Show Form
$drc = $form.ShowDialog()

Here is Code that does not work:

# Define Form
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(260,250)
$Form.Text = "ToolBox"
$form.StartPosition = "CenterScreen"

# Create Button Function
function CreateButton ($ButtonCommand, $ButtonName, $ButtonLocX, $ButtonLocY, $ButtonSizeX, $ButtonSizeY)
{
$btn = New-Object System.Windows.Forms.Button
$btn.add_click($ButtonCommand)
$btn.Text = $ButtonName
$btn.Location = New-Object System.Drawing.Size($ButtonLocX,$ButtonLocY)
$btn.Size = New-Object System.Drawing.Size($ButtonSizeX,$ButtonSizeY)
$form.Controls.Add($btn)
}


$Global:Test="C:\Windows\system32\mmc.exe"
$Command={Start-Process -FilePath $Global:Test}
$Text='MMC'
$LocationX=20
$LocationY=10
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Global:Test="C:\Windows\regedit.exe"
$Command={Start-Process -FilePath $Global:Test}
$Text='Regedit'
$LocationX=20
$LocationY=70
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Global:Test="C:\Windows\System32\cmd.exe"
$Command={Start-Process -FilePath $Global:Test}
$Text='CMD'
$LocationX=20
$LocationY=100
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

#Show Form
$drc = $form.ShowDialog()

The first example creates a form with 3 buttons. When I click each button different applications get started. The second example creates a form with 3 buttons. When I click each button "CMD.EXE" gets opened. When I change the CMD.EXE with the MMC.EXE block in the code each button will open the MMC. Can anyone explain whats going on?

4

2 回答 2

2

在命令块执行之前,块内的$Global:Test变量$Command不会得到解析。即,当它创建命令块时,它实际上包含$Global:Test在其中,而不是您在前一行设置的值。

因此,当一个$Command块执行时,它会解析变量,该变量是根据$Global:Test=脚本中的最后一个赋值设置的。

于 2015-06-26T19:56:46.113 回答
2

本质上你想要的是,同一脚本块的不同实例会产生不同的结果。为此,您必须将不同的上下文附加到每个脚本块实例。一种方法是将脚本块绑定到模块。为此,您可以使用GetNewClosure()脚本块实例方法:

$ScriptBlockWithContext=$ScriptBlock.GetNewClosure()

此方法将创建新模块并将所有当前范围变量捕获到其中。如果您想更好地控制要捕获的内容,则可以自己创建模块:

$Module=New-Module {$Context=$Global:Context}
$ScriptBlockWithContext=$Module.NewBoundScriptBlock($ScriptBlock)

如果要引用局部变量,则可以将它们作为附加参数传递给 New-Module cmdlet:

$Module=New-Module {param($Context)} $Context
$ScriptBlockWithContext=$Module.NewBoundScriptBlock($ScriptBlock)

或者,您可以在创建后调用运算符&.在模块范围内调用任意代码。特别是,您可以使用New-VariableSet-Variablecmdlet 在模块范围内设置变量:

$Module=New-Module {}
& $Module New-Variable Context $Context
$ScriptBlockWithContext=$Module.NewBoundScriptBlock($ScriptBlock)

这里对您的代码进行简单修改以使其工作:

# Define Form
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(260,250)
$Form.Text = "ToolBox"
$form.StartPosition = "CenterScreen"

# Create Button Function
function CreateButton ($ButtonCommand, $ButtonName, $ButtonLocX, $ButtonLocY, $ButtonSizeX, $ButtonSizeY)
{
$btn = New-Object System.Windows.Forms.Button
$btn.add_click($ButtonCommand)
$btn.Text = $ButtonName
$btn.Location = New-Object System.Drawing.Size($ButtonLocX,$ButtonLocY)
$btn.Size = New-Object System.Drawing.Size($ButtonSizeX,$ButtonSizeY)
$form.Controls.Add($btn)
}

$CaptureCommand={param($Test)}
$CommonCommand={Start-Process -FilePath $Test}

$Module=New-Module $CaptureCommand "C:\Windows\system32\mmc.exe"
$Command=$Module.NewBoundScriptBlock($CommonCommand)
$Text='MMC'
$LocationX=20
$LocationY=10
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Module=New-Module $CaptureCommand "C:\Windows\regedit.exe"
$Command=$Module.NewBoundScriptBlock($CommonCommand)
$Text='Regedit'
$LocationX=20
$LocationY=70
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

$Module=New-Module $CaptureCommand "C:\Windows\System32\cmd.exe"
$Command=$Module.NewBoundScriptBlock($CommonCommand)
$Text='CMD'
$LocationX=20
$LocationY=100
$SizeX=200
$SizeY=30
CreateButton $Command $Text $LocationX $LocationY $SizeX $SizeY

#Show Form
$drc = $form.ShowDialog()
于 2015-06-26T20:38:54.460 回答