Need help getting output from listbox selections

I have cobbled together a script I want to use to apply windows images to a machine using a script with the following code running on a winPE boot disk. This isn’t the entire script; it’s just the part I’m having issues with.

I can successfully populate the first listbox, which is the list of the .wim files on c:, and I can successfully populate the second listbox, which is a list of the indexes of the selected .wim file from the first listbox.

The command I’ll be using is "dism /apply-image /imagefile:c:$selectedwimfile /index:$selectedindex /applydir:w:"

What I can’t seem to figure out how to do, is to return the actual name of the wimfile from $selectedwimfile, and to return only the index number of the selected index with $selectedindex, so the command to write the .wim file will in essence run as "dism /apply-image /imagefile:c:\Win7-32bit.wim /index:2 /applydir:w:", with the name and index being chosen by the input from the list boxes of course.

Here’s my code so far: btw, the “write host” button is just for testing purposes, and serves no purpose other than for me to see results of different things.

# Set up the form ===========================================================
 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.width = 500
$objForm.height = 500
$objForm.Text = ”wim file test part deux”

# Add the listbox containing wim files in c:\ =============================
 
$objWimListBox = New-Object System.Windows.Forms.Listbox
$objWimListBox.Location = New-Object System.Drawing.Size(50,40)
$objWimListBox.Size = New-Object System.Drawing.Size(260,120)

$objForm.Controls.Add($objWimListBox)

$wimfiles = Get-ChildItem c:\*.wim
 
foreach ($wimfile in $wimfiles)
  {   
      [Void] $objWimListBox.Items.Add($wimfile.Name)
  }

# Add the Indexes listbox  ================================================
 
$objIndexListBox = New-Object System.Windows.Forms.Listbox
$objIndexListBox.Location = New-Object System.Drawing.Size(50,175)
$objIndexListBox.Size = New-Object System.Drawing.Size(260,200)
$objForm.Controls.Add($objIndexListbox)

# Add the subroutine for retrieving instances ===============================
 
$objWimListBox.Add_Click(
  {
    $selectedwimfile = $objWimListBox.SelectedItem
    $wimfiles = Get-WindowsImage -ImagePath c:\$selectedwimfile
    $objIndexListBox.Items.Clear()
       
      if ($wimfiles –ne $Null)
      {
        foreach ($index in $wimfiles)
        {  
			$itemText = "$($index.ImageIndex) - $($index.ImageDescription)"
			$objIndexListBox.Items.Add($itemText)
			$selectedindex = $objIndexListBox.SelectedItem
		}
      }
    })

$Button = new-object System.Windows.Forms.Button
$Button.Location = new-object System.Drawing.Size(130,400)
$Button.Size = new-object System.Drawing.Size(100,40)
$Button.Text = "Write Host"
$Button.Add_Click({Write-Host "You have chosen" $objWimListBox.SelectedItem "with index" $objIndexListBox.selecteditem})
$objform.Controls.Add($Button)

# Activate the form =========================================================
 
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

In the script blocks you’re assigning to Add_Click events, any variable assignments are local to that script block, and don’t make it out to the code which called $objForm.ShowDialog(). The quick and dirty fix for this is to make assignments to script-scoped variables instead:

$selectedwimfile = $objWimListBox.SelectedItem

# becomes this:

$script:selectedwimfile = $objWimListBox.SelectedItem

# etc

Thanks for the extra set of eyes.

This works for the portion of $selectedwimfile, and when I call it with something like write-host $selectedwimfile, I get the output I’m looking for, such as “Win7-32bit.wim”.

But, it doesn’t work for $selectedindex. No matter what I do, $selectedindex is null.

I discovered I can also grab what I need by calling $objWimListBox.SelectedItem. For instance, I can do a “write-host $objWimListBox.SelectedItem” and get the name of the wim file selected, such as “Win7-32bit.wim”.

I can also do a “write-host $objIndexListBox.SelectedItem”, and it gives me something like “4 - Dell E5550”, meaning index number 4 of the wim file, and Dell E5550 being the index description.

I believe I know why I’m having issues grabbing just the index number to return to a command to write the wim file to a machine. I think it’s because when I’m populating each line of the listbox, I’m lumping 2 values into 1 variable as text?

I’ve tried to work around this by trimming the output of $objIndexListBox.SelectedItem, but, in winPE running it in the command, it chokes on the trim condition.

Hope this makes sense… it at least shows that I’m not exactly as familiar with powershell and coding as I’d like/need to be…

I’m not really sure what this bit of code is trying to accomplish:

      if ($wimfiles –ne $Null)
      {
        foreach ($index in $wimfiles)
        {  
			$itemText = "$($index.ImageIndex) - $($index.ImageDescription)"
			$objIndexListBox.Items.Add($itemText)
			$selectedindex = $objIndexListBox.SelectedItem
		}
      }

That is supposed to be the bit of code that populates the 2nd listbox with a list of indexes contained in the wim file from the selected wim file from the 1st list box.

What I am wanting to happen, is:
Script runs, and populates first list box with all *.wim files from a directory.
Selecting a .wim file from the first list box, causes the second list box to be populated with the indexes contained inside of the selected .wim file

I then want to be able to pass this to dism, as in:
dism /apply-image /imagefile:u:$selectedwimfile /index:$selectedindex /applydir:c:\

Brian, that is reasonable. Since you are combining the ImageIndex and the ImageDescription into your listbox value, the result when you select it is that combination, not just the index number which is what dism is expecting. You could handle this two different ways. You could either use multiple columns in your list box and store the index number separate from the description, or you can just parse your returned value so that you get just the index number.

In the example below we take the selected index which is the string you built, use index of to find out where the " - " (space dash space) characters are in the string, use substring to take everything from character 0 to up to the place where it found " - ", and lastly strictly cast it as an integer before storing it in the $selectedindexnumber variable. You should then be able to use that in your DISM command

$selectedindex = "4 - Dell E5550"
$selectedindexnumber = [int]$selectedindex.Substring(0,$selectedindex.Indexof(" - "))

dism /apply-image /imagefile:u:\$selectedwimfile /index:$selectedindexnumber /applydir:c:\

weird… in powershell console, what you suggested works great.

But, when I add it to the script, it keeps barking at me with:
Exception calling “Substring” with “2” argument(s): “Length cannot be less than zero.
Parameter name: length”
At C:\Users\someuser\Desktop\test.ps1:58 char:1

  • $selectedindexnumber = [int]$selectedindex.Substring(0,$selectedindex.Indexof(" …
  •   + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
      + FullyQualifiedErrorId : ArgumentOutOfRangeException

Ok, some more testing…

If I do

Write-Host $objIndexListBox.SelectedItem.Remove($objIndexListBox.SelectedItem.IndexOf(’ '))
then everything works like it should; it prints to the host the actual number of the index.

But, if I try to assign it to anything like

$selectedindexnumber = $objIndexListBox.SelectedItem.Remove($objIndexListBox.SelectedItem.IndexOf(’ '))
, I get “You cannot call a method on a null-valued expression”.

I can write it, but I can’t store it?

Ok… I think I have this working now. I’ve tested the output, and it’s giving me what I need. Curtis, I switched to a listview as you suggested, and while it took some playing around with to get what I wanted…

I think this bit of code is what I understand, draws the value into something I can assign to a variable? Not sure if I worded that right… but, at this point, it’s all I know.

$listView.Add_Click({foreach($item in $listView.SelectedItems)
{ 
   $script:indexnumber = $item.Text
}
})

So, here’s the entire code, and it’s now working and giving me the output of what I’m wanting, although it’s not using listbox, but rather listview.

# Set up the form ===========================================================
 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.width = 500
$objForm.height = 500
$objForm.Text = ”wim file test part deux”

# Add the listbox containing wim files in u:\ =============================
 
$objWimListBox = New-Object System.Windows.Forms.Listbox
$objWimListBox.Location = New-Object System.Drawing.Size(50,40)
$objWimListBox.Size = New-Object System.Drawing.Size(260,120)

$objForm.Controls.Add($objWimListBox)

$wimfiles = Get-ChildItem u:\*.wim
 
foreach ($wimfile in $wimfiles)
  {   
      [Void] $objWimListBox.Items.Add($wimfile.Name)
  }

# Add the Indexes listbox  ================================================
 
$listView = New-Object System.Windows.Forms.ListView
$listView.View = 'Details'
$listView.Location = New-Object System.Drawing.Size(50,175)
$listView.Size = New-Object System.Drawing.Size(260,200)
#$listView.Width = 386
#$listView.Height = 272
$listView.GridLines = 0
$listView.Scrollable = 1
$listView.FullRowSelect = 1 
$listView.HideSelection = 0
$listView.Columns.Add('Index', 40)
$listView.Columns.Add('Description', 170)
$objForm.Controls.Add($listView)

# Add the subroutine for retrieving instances ===============================
 
$objWimListBox.Add_Click(
  {
  	$listView.Items.Clear()
    $script:selectedwimfile = $objWimListBox.SelectedItem
    $wimfiles = Get-WindowsImage -ImagePath u:\$selectedwimfile
    $indexnumber = @()   
	   if ($wimfiles –ne $Null)
      {
        foreach ($index in $wimfiles)
        {  
			ForEach ($line in $index) {
    		$item = New-Object System.Windows.Forms.ListViewItem($line.ImageIndex)
    		$item.SubItems.Add($line.ImageDescription)
    		$listView.Items.Add($item)
		}
			
		}
      }
	  
    })
$listView.Add_Click({foreach($item in $listView.SelectedItems)
{ 
   $script:indexnumber = $item.Text
}
})

$Button = new-object System.Windows.Forms.Button
$Button.Location = new-object System.Drawing.Size(130,400)
$Button.Size = new-object System.Drawing.Size(100,40)
$Button.Text = "Write Host"
$Button.Add_Click({Write-Host "You have chosen the wim file of $selectedwimfile with index $indexnumber"})
$objform.Controls.Add($Button)

# Activate the form =========================================================
 
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()