2

See code below. Put both files in the same directory and run Form1.ps1 from the PS ISE

As you can see, the (local) variable $localVar is defined in the event handler $button2_Click. As such, I assumed $localVar would not/could not exist outside the scope of $button2_Click with scope defined by the braces that define the event handler.

However, as you can see, I use the contents of $localVar to load $textbox2.Text in the function fA. When you click the Test button, both textboxes display the contents of $localVar

What's going on? Why is $button2_Click's $localVar accessible from within fA?

Form1.ps1

function fA
{
    $textbox2.Text = $localVar
}

$button2_Click = 
{
    $localVar = "set in `$button2_Click"
    $textbox1.Text = $localVar
    fA
}

. (Join-Path $PSScriptRoot 'Form1.designer.ps1')

$textbox1.Text = ""
$Form1.ShowDialog()

Form1.designer.ps1

[void][System.Reflection.Assembly]::Load('System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][System.Reflection.Assembly]::Load('System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
$Form1 = New-Object -TypeName System.Windows.Forms.Form
[System.Windows.Forms.Button]$button2 = $null
[System.Windows.Forms.TextBox]$textBox1 = $null
[System.Windows.Forms.TextBox]$textBox2 = $null
[System.Windows.Forms.Label]$label1 = $null
[System.Windows.Forms.Label]$label2 = $null
[System.Windows.Forms.Button]$button1 = $null
function InitializeComponent
{
$button2 = (New-Object -TypeName System.Windows.Forms.Button)
$textBox1 = (New-Object -TypeName System.Windows.Forms.TextBox)
$textBox2 = (New-Object -TypeName System.Windows.Forms.TextBox)
$label1 = (New-Object -TypeName System.Windows.Forms.Label)
$label2 = (New-Object -TypeName System.Windows.Forms.Label)
$Form1.SuspendLayout()
#
#button2
#
$button2.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]148,[System.Int32]12))
$button2.Name = [System.String]'button2'
$button2.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]77,[System.Int32]36))
$button2.TabIndex = [System.Int32]0
$button2.Text = [System.String]'Test'
$button2.UseVisualStyleBackColor = $true
$button2.add_Click($button2_Click)
#
#textBox1
#
$textBox1.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]67,[System.Int32]69))
$textBox1.Name = [System.String]'textBox1'
$textBox1.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]106,[System.Int32]20))
$textBox1.TabIndex = [System.Int32]1
#
#textBox2
#
$textBox2.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]247,[System.Int32]69))
$textBox2.Name = [System.String]'textBox2'
$textBox2.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]106,[System.Int32]20))
$textBox2.TabIndex = [System.Int32]2
#
#label1
#
$label1.AutoSize = $true
$label1.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]12,[System.Int32]72))
$label1.Name = [System.String]'label1'
$label1.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]47,[System.Int32]13))
$label1.TabIndex = [System.Int32]3
$label1.Text = [System.String]'textbox1'
#
#label2
#
$label2.AutoSize = $true
$label2.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]194,[System.Int32]72))
$label2.Name = [System.String]'label2'
$label2.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]47,[System.Int32]13))
$label2.TabIndex = [System.Int32]4
$label2.Text = [System.String]'textbox2'
#
#Form1
#
$Form1.ClientSize = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]374,[System.Int32]110))
$Form1.Controls.Add($label2)
$Form1.Controls.Add($label1)
$Form1.Controls.Add($textBox2)
$Form1.Controls.Add($textBox1)
$Form1.Controls.Add($button2)
$Form1.Name = [System.String]'Form1'
$Form1.ResumeLayout($false)
$Form1.PerformLayout()
Add-Member -InputObject $Form1 -Name base -Value $base -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name button2 -Value $button2 -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name textBox1 -Value $textBox1 -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name textBox2 -Value $textBox2 -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name label1 -Value $label1 -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name label2 -Value $label2 -MemberType NoteProperty
Add-Member -InputObject $Form1 -Name button1 -Value $button1 -MemberType NoteProperty
}
. InitializeComponent
VA systems engineer
  • 2,856
  • 2
  • 14
  • 38
  • https://superuser.com/a/778089/591793 – Jacob Colvin Oct 02 '18 at 16:28
  • 1
    Here is a good read about [PS variable scope](https://4sysops.com/archives/the-powershell-variable-scope/). The available scope options are: Private, Local, Script, and Global. If you wanted to ensure that your `$localVar` was only available in the its Local Scope you would need to change it to: `$Private:localVar` . Also if you do not destroy your variables at the end of your script, it will live in the Shell - Global Scope – jrider Oct 02 '18 at 16:29
  • @NovaSysEng, can I suggest you remove the `Form1.designer.ps1` code, as it is incidental to your question and creates a distraction? The heart of you question is that when your `$button2_Click` script block is called - in whatever way - calling function `fA` from there lets `fA` see the calling script block's `$localVar` variable's (value), which is not what you expect. – mklement0 Oct 02 '18 at 18:36
  • disagree - I want people to be able to run the code to see the issue. Form1.designer.ps1 ain't that much code – VA systems engineer Oct 03 '18 at 11:40

2 Answers2

4

Function fA sees your variable, because it runs in a child scope of the script block in which $localVar was created - this is general PowerShell behavior, and not specific to the ISE.

When you create a variable with $localVar = ..., it is local in the following sense:

  • visible and directly modifiable in the same scope, but not in any parent scopes.

  • visible in all child scopes, but not directly modifiable there.

    • Caveat: Functions imported from modules run in a separate scope domain (a.k.a. session (sub)state) that only shares the global scope as an ancestor with code running outside of modules and in other modules. Therefore, a module imported from a function does not see its caller's variables (and functions and aliases), if the caller is in non-module code (other than the global scope) or from a different module.
      Another way of putting it: a function imported from a module does not run in a child scope of a non-module caller (outside the global scope), and therefore doesn't see that caller's definitions.

    • You can use the $private: scope modifier to prevent child scopes from seeing a variable.

    • If you assign to a variable (by name only) that was originally created in a parent scope, (e.g., $localVar = ...), you'll instead create a new, local variable, in the current scope, which shadows the original variable.

      • It is possible to modify a parent scope's variables, but you need to use either Set-Variable -Scope or a scope modifier such as $script: (see links below).

For more information, see:

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • nice, concise, summary. I'm only sorry that I was unable to pull it out of the Get-Help about_Scopes for myself. I guess I've always looked at functions as being black boxes that can't see variables established elsewhere unless they are **explicitly** defined as global. The idea that a variable is implicitly accessible (even when the parent copy is protected from change) seems sneaky :-) – VA systems engineer Oct 03 '18 at 11:44
  • Thanks, @NovaSysEng. Yes, the help topic on scopes could use some improvement. And that child scopes see variables from parent scopes is indeed surprising. Honestly, I'm not sure why they decided to do it that way. – mklement0 Oct 03 '18 at 13:08
0

Hello So in ISE you have Powershell Tabs and Script Tabs.

A Powershell Tab is like running a single Powershell console. Each Script tab inside that powershell tab is using that console. So if in one script you define

$Hello = "TEST"

then run that in ise. Then in another script tab in the same powershell tab you run

$Hello

the output would be "TEST"

This is called Scope. Basically what variables code can see. All variable created in a powershell instance are always accessible in the rest of that instance

So lets get a little deeper and go over you example

function fA
{
    $textbox2.Text = $localVar
}

$button2_Click = 
{
    $localVar = "123"
    $textbox1.Text = $localVar
    fA
}

$localVar is created on click for the first time. The scope will carry on from that to anything created after it. So FA is called and $localvar is carried to FA scope. Textbox1 and 2 show 123. But lets show how there is still scope to this and its not global.

function TB1
{
    $localVar = "456"
    $textbox1.Text = $localVar # Will output 456
}

function TB2{
    $textbox2.Text = $localVar # Will output 123
}
$button2_Click = 
{
    $localVar = "123"
    TB1
    TB2
    fA
}

$localVar created for first time. Passed to the first function $localvar is still 123 but in the function its changed to 456. Once it leaves there it goes to second function where its $Localvar will equal 123. The variable changed in the function did not effect the variable that was in its parent aka the caller button2_click

ArcSet
  • 6,518
  • 1
  • 20
  • 34
  • My question is not why a variable defined in one script tab is accessible in another script tab. The perspective between script tabs is not my concern. My question is why is a variable defined in one script block (my `$button2_Click`) accessible inside another script block (my function `fA`)?. If PowerShell doesn't enforce scope between script blocks without having to explicitly define each variable as `local`, then why worry about scope at all?. Just assume PowerShell views all variables as `$global`? This doesn't make sense. I think I'm missing something more fundamental – VA systems engineer Oct 02 '18 at 17:20