Populate combox with mapped drives - winforms

Trying to populate a combo box with mapped drive letters (name) and FQDN (Root). I think I have most of the code but the combo box entries includes coded entries.
I'm not only curious about how to fix this but why the results are entered this way. Running this via command line does not display results this way.
NOTE: I'm also using a function to populate the combo box.
Code to retrieve mapped drives
Load-ComboBox -ComboBox $cboDomain -Items (Get-PSDrive -PSProvider FileSystem | Select-Object name, #{ n = "Root"; e = { if ($_.DisplayRoot -eq $null) { $_.Root } else { $_.DisplayRoot } } })
Function to load combo box
function Load-ComboBox{
Param (
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
[System.Windows.Forms.ComboBox]$ComboBox,
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
$Items,
[Parameter(Mandatory=$false)]
[string]$DisplayMember,
[switch]$Append
)
if(-not $Append)
{
$ComboBox.Items.Clear()
}
if($Items -is [Object[]])
{
$ComboBox.Items.AddRange($Items)
}
elseif ($Items -is [System.Collections.IEnumerable])
{
$ComboBox.BeginUpdate()
foreach($obj in $Items)
{
$ComboBox.Items.Add($obj)
}
$ComboBox.EndUpdate()
}
else
{
$ComboBox.Items.Add($Items)
}
$ComboBox.DisplayMember = $DisplayMember}
The entries look like;
#{Name=C; Root=C:}
#Name=S; Root=\\server\share}
I want it to look like;
C<-tab->C:\
S<-tab->\\server\share
*Sorry couldn't figure out how to actually insert tab

Since you are sending Objects to the function (Select-Object returns objects), and not an array of tab separated strings, the function would work if you call it like this:
$drives = (Get-PSDrive -PSProvider FileSystem | ForEach-Object {
$root = if ($_.DisplayRoot -eq $null) { $_.Root } else { $_.DisplayRoot }
# output a tab-separated string that gets collected in the $drives variable
"$($_.Name)`t$root"
})
Load-ComboBox -ComboBox $cboDomain -Items $drives
Hope that explains

Related

Powershell winform ListView right mouse button context menu binding to specific ListView item

I'm trying to create clicks for right mouse button which are specific to each item in the listview to be able to execute commands on remote pc. I just don't know how assign the separate context menu to a specific ListViewItem.
I have tried to create form also with an array but I have bumped to the same problem and I don't know how to formulate the problem to search for it on internet.
Thanks for help!
Here is the code:
## Set up the environment
Add-Type -AssemblyName System.Windows.Forms
$LastColumnClicked = 0 # tracks the last column number that was clicked
$LastColumnAscending = $false # tracks the direction of the last sort of this column
## Create a form and a ListView
$Form = New-Object System.Windows.Forms.Form
$ListView = New-Object System.Windows.Forms.ListView
## Configure the form
$Form.Text = "Computer List"
## Configure the ListView
$ListView.View = [System.Windows.Forms.View]::Details
$ListView.Width = $Form.ClientRectangle.Width
$ListView.Height = $Form.ClientRectangle.Height
$ListView.Anchor = "Top, Left, Right, Bottom"
# Add the ListView to the Form
$Form.Controls.Add($ListView)
# Add columns to the ListView
$ListView.Columns.Add("Computer Name", -2) | Out-Null
$ListView.Columns.Add(" Test") | Out-Null
# Add list items
$contextMenuStrip = [System.Windows.Forms.ContextMenuStrip]#{}
$listView.Font = 'Microsoft Sans Serif,10'
##Computer1##
$ListViewItem = New-Object System.Windows.Forms.ListViewItem("computer1")
$click1 = {Invoke-Command -UseSSL -ComputerName computer1 }
$click2 = {Invoke-Command -UseSSL -ComputerName computer1 }
$item1 = $contextMenuStrip.Items.Add("Run 1")
$item1.add_Click($click1)
$item2 = $contextMenuStrip.Items.Add("Run 2")
$item2.add_Click($click2)
$listView.Add_MouseClick({param($sender,$e)
if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Right){
if ($listView.FocusedItem.GetBounds(
[System.Windows.Forms.ItemBoundsPortion]::Entire).Contains($e.Location)){
$contextMenuStrip.Show([System.Windows.Forms.Cursor]::Position)
}
}
})
$ListView.Items.Add($ListViewItem) | Out-Null
##computer2##
$contextMenuStrip = [System.Windows.Forms.ContextMenuStrip]#{}
$ListViewItem = New-Object System.Windows.Forms.ListViewItem("computer2")
$click1 = {Invoke-Command -UseSSL -ComputerName computer2 }
$click2 = {Invoke-Command -UseSSL -ComputerName computer2 }
$item1 = $contextMenuStrip.Items.Add("Run 3")
$item1.add_Click($click1)
$item2 = $contextMenuStrip.Items.Add("Run 4")
$item2.add_Click($click2)
$listView.Add_MouseClick({param($sender,$e)
if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Right){
if ($listView.FocusedItem.GetBounds(
[System.Windows.Forms.ItemBoundsPortion]::Entire).Contains($e.Location)){
$contextMenuStrip.Show([System.Windows.Forms.Cursor]::Position)
}
}
})
$ListView.Items.Add($ListViewItem) | Out-Null
## Set up the event handler
$ListView.add_ColumnClick({SortListView $_.Column})
## Event handler
function SortListView
{
param([parameter(Position=0)][UInt32]$Column)
$Numeric = $true # determine how to sort
# if the user clicked the same column that was clicked last time, reverse its sort order. otherwise, reset for normal ascending sort
if($Script:LastColumnClicked -eq $Column)
{
$Script:LastColumnAscending = -not $Script:LastColumnAscending
}
else
{
$Script:LastColumnAscending = $true
}
$Script:LastColumnClicked = $Column
$ListItems = #(#(#())) # three-dimensional array; column 1 indexes the other columns, column 2 is the value to be sorted on, and column 3 is the System.Windows.Forms.ListViewItem object
foreach($ListItem in $ListView.Items)
{
# if all items are numeric, can use a numeric sort
if($Numeric -ne $false) # nothing can set this back to true, so don't process unnecessarily
{
try
{
$Test = [Double]$ListItem.SubItems[[int]$Column].Text
}
catch
{
$Numeric = $false # a non-numeric item was found, so sort will occur as a string
}
}
$ListItems += ,#($ListItem.SubItems[[int]$Column].Text,$ListItem)
}
# create the expression that will be evaluated for sorting
$EvalExpression = {
if($Numeric)
{ return [Double]$_[0] }
else
{ return [String]$_[0] }
}
# all information is gathered; perform the sort
$ListItems = $ListItems | Sort-Object -Property #{Expression=$EvalExpression; Ascending=$Script:LastColumnAscending}
## the list is sorted; display it in the listview
$ListView.BeginUpdate()
$ListView.Items.Clear()
foreach($ListItem in $ListItems)
{
$ListView.Items.Add($ListItem[1])
}
$ListView.EndUpdate()
}
## Show the form
$Response = $Form.ShowDialog()
})
$ListView.Items.Add($ListViewItem) | Out-Null

Powershell: PSCustomObject array as parameter in function gets changed unexpectedly

In the simplified PS code below, I don't understand why the $MyPeople array gets changed after calling the changeData function. This array variable should just be made a copy of, and I expect the function to return another array variable into $UpdatedPeople and not touch $MyPeople:
function changeData {
Param ([PSCustomObject[]]$people)
$changed_people = $people
$changed_people[0].Name = "NEW NAME"
return $changed_people
}
# Original data:
$Person1 = [PSCustomObject]#{
Name = "First Person"
ID = 1
}
$Person2 = [PSCustomObject]#{
Name = "Second Person"
ID = 2
}
$MyPeople = $Person1,$Person2
"`$MyPeople[0] =`t`t" + $MyPeople[0]
"`n# Updating data..."
$UpdatedPeople = changeData($MyPeople)
"`$UpdatedPeople[0] =`t" + $UpdatedPeople[0]
"`$MyPeople[0] =`t`t" + $MyPeople[0]
Console output:
$MyPeople[0] = #{Name=First Person; ID=1}
# Updating data...
$UpdatedPeople[0] = #{Name=NEW NAME; ID=1}
$MyPeople[0] = #{Name=NEW NAME; ID=1}
Thanks!
PSObject2 = PSObject1 is not a copy but a reference. You need to clone or copy the original object using a method designed for that purpose.
function changeData {
Param ([PSCustomObject[]]$people)
$changed_people = $people | Foreach-Object {$_.PSObject.Copy()}
$changed_people[0].Name = "NEW NAME"
return $changed_people
}
The technique above is simplistic and should work here. However, it is not a deep clone. So if your psobject properties contain other psobjects, you will need to look into doing a deep clone.
We can clone the PSCustomObject. We will create a new PSObject and enumerate through the psobject given as parameter and add them one by one to the shallow copy.
function changeData {
Param ([PSCustomObject[]]$people)
$changed_people = New-Object PSobject -Property #{}
$people.psobject.properties | ForEach {
$changed_people | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value
}
$changed_people[0].Name = 'NEW NAME'
return $changed_people
}
Or use another method by #AdminOfThings

Runspace scriptblock array cannot be passed

I'm tinkering with the code below. It should create a PoshRSJob and run the function foo again in the runspace there.
I want to be able to turn the $list parameter into an [array] or [string[]], but when I do it throws errors. I considered flattening my array into a string, but if I change $list3 to include a space or comma in the string it also throws an error. I believe it is this line that is causing the issue, but I don't know why or what to do to circumvent this issue:
ScriptBlock = [scriptblock]::Create("`$_ | $($PSCmdlet.MyInvocation.MyCommand.Name) -Parallel:`$false -fn:$fn -sqlQuery:$SQLQuery -option:$option -List:$List")
Code:
function foo {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("ComputerName")]
[PSObject] $InputObject,
[switch] $Parallel = $true,
[string] $fn,
[string] $sqlQuery = "none",
[string] $option = "none",
[int] $number,
[int] $Throttle = 100,
#Want this to be [array] or [string[]] :
[string] $List = "none"
)
begin {
$batch = [System.Guid]::NewGuid().Guid #run all jobs under same batch number
}
process {
if ($Global:debugging -eq $true){$host.ui.WriteDebugLine("fn:$fn | SQLQuery:$sqlQuery")}
if (!$Parallel) {
$server = $InputObject.name
switch($fn){
Manage{ return $list }
} #end switch
} else {#region Parallel run
$jobArguments = #{
Throttle = $Throttle
Batch = $batch
FunctionsToLoad = $PSCmdlet.MyInvocation.MyCommand.Name
#This is the problematic line:
ScriptBlock = [scriptblock]::Create("`$_ | $($PSCmdlet.MyInvocation.MyCommand.Name) -Parallel:`$false -fn:$fn -sqlQuery:$SQLQuery -option:$option -List:$List")
}
if ($_ -and $_ -isnot [string]) { $serverName=$_ } else { $serverName=$InputObject.name }
#(if ($_ -and $_ -isnot [string]) { $_ } else { $InputObject }) | Start-RSJob #jobArguments | Out-Null
} #endregion
}
end {#region Wait for results and return them
if ($Parallel) {
Get-RSJob -batch $batch | Wait-RSJob -ShowProgress | Out-Null
}#endregion
}
}
$obj = New-Object -TypeName PSObject -Property #{
Name = 'server1'
Other = 'other'
}
$list2 = $obj
$list3 = "item1-item2"
$list2 | foo -fn 'Manage' -number 2 -option Q -List $list3
This is the error:
The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
Does anyone know how to get this working so I can pass a list into the runspace?

ListView Sort doesn't work OnClick (PowerShell)

I have a ListView on a form built in PowerShell which I just can't get sorted.
If I call $LV.Sort() from the command line (while the form is not shown) it works perfectly fine. However if I try to call it in an OnClick event on anything on the form it doesn't. I've put flags in and I know that section of code is running, it's just not doing anything or flagging any errors.
Obviously $LV.Sorting = "Ascending" is set otherwise it wouldn't be sorting at all, and I can change the items so the reference to the ListView is fine. Also tried invoking in case of a threading issue but no different.
Annoyingly this is a tricky subject to research due to so many people forgetting to set the Sorting property.
EDIT: Not sure if relevant but the form is called with ShowDialog()
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$form = New-Object System.Windows.Forms.Form
$LVserverlist = New-Object System.Windows.Forms.ListView
$form.Controls.Add($LVserverlist)
$LVserverlist.Dock = "Fill"
$LVserverlist.View = "Details"
$LVserverlist.Sorting = "Ascending"
$LVserverlist.Add_ColumnClick({ $LVserverlist.sort(); $LVserverlist.Items[0].Text = "Working?" })
$LVserverlist.Columns.Add("Name", 200, "Left") | Out-Null
0..10 | Foreach-Object {
$LVItem = New-Object System.Windows.Forms.ListViewItem
$LVItem.Text = (Get-Random -Maximum 1000).ToString()
$LVserverlist.Items.Add($LVItem) | Out-Null
}
$form.ShowDialog()
You can try with custom function Sort-ListViewColumn
$MyListView.Add_ColumnClick({
param($sender,$e)
# Sort as Text String (not [int])
Sort-ListViewColumn -ListView $this -ColumnIndex $e.column
})
full function code :
function Sort-ListViewColumn
{
<#
.SYNOPSIS
Sort the ListView's item using the specified column.
.DESCRIPTION
Sort the ListView's item using the specified column.
This function uses Add-Type to define a class that sort the items.
The ListView's Tag property is used to keep track of the sorting.
.PARAMETER ListView
The ListView control to sort.
.PARAMETER ColumnIndex
The index of the column to use for sorting.
.PARAMETER SortOrder
The direction to sort the items. If not specified or set to None, it will toggle.
.EXAMPLE
Sort-ListViewColumn -ListView $listview1 -ColumnIndex 0
.NOTES
SAPIEN Technologies, Inc.
http://www.sapien.com/
#>
param (
[ValidateNotNull()]
[Parameter(Mandatory = $true)]
[System.Windows.Forms.ListView]$ListView,
[Parameter(Mandatory = $true)]
[int]$ColumnIndex,
[System.Windows.Forms.SortOrder]$SortOrder = 'None')
if (($ListView.Items.Count -eq 0) -or ($ColumnIndex -lt 0) -or ($ColumnIndex -ge $ListView.Columns.Count))
{
return;
}
#region Define ListViewItemComparer
try
{
$local:type = [ListViewItemComparer]
}
catch
{
Add-Type -ReferencedAssemblies ('System.Windows.Forms') -TypeDefinition #"
using System;
using System.Windows.Forms;
using System.Collections;
public class ListViewItemComparer : IComparer
{
public int column;
public SortOrder sortOrder;
public ListViewItemComparer()
{
column = 0;
sortOrder = SortOrder.Ascending;
}
public ListViewItemComparer(int column, SortOrder sort)
{
this.column = column;
sortOrder = sort;
}
public int Compare(object x, object y)
{
if(column >= ((ListViewItem)x).SubItems.Count)
return sortOrder == SortOrder.Ascending ? -1 : 1;
if(column >= ((ListViewItem)y).SubItems.Count)
return sortOrder == SortOrder.Ascending ? 1 : -1;
if(sortOrder == SortOrder.Ascending)
return String.Compare(((ListViewItem)x).SubItems[column].Text, ((ListViewItem)y).SubItems[column].Text);
else
return String.Compare(((ListViewItem)y).SubItems[column].Text, ((ListViewItem)x).SubItems[column].Text);
}
}
"# | Out-Null
}
#endregion
if ($ListView.Tag -is [ListViewItemComparer])
{
#Toggle the Sort Order
if ($SortOrder -eq [System.Windows.Forms.SortOrder]::None)
{
if ($ListView.Tag.column -eq $ColumnIndex -and $ListView.Tag.sortOrder -eq 'Ascending')
{
$ListView.Tag.sortOrder = 'Descending'
}
else
{
$ListView.Tag.sortOrder = 'Ascending'
}
}
else
{
$ListView.Tag.sortOrder = $SortOrder
}
$ListView.Tag.column = $ColumnIndex
$ListView.Sort()#Sort the items
}
else
{
if ($Sort -eq [System.Windows.Forms.SortOrder]::None)
{
$Sort = [System.Windows.Forms.SortOrder]::Ascending
}
#Set to Tag because for some reason in PowerShell ListViewItemSorter prop returns null
$ListView.Tag = New-Object ListViewItemComparer ($ColumnIndex, $SortOrder)
$ListView.ListViewItemSorter = $ListView.Tag #Automatically sorts
}
}
Never did find a solution to getting Sort() to work, ended up with this workaround (which is a bit more flexible anyway):
Param
(
[System.Windows.Forms.ListView]$sender,
$column
)
$temp = $sender.Items | Foreach-Object { $_ }
$sender.Items.Clear()
$sender.Items.AddRange(($temp | Sort-Object -Property #{ Expression={ $_.SubItems[$column].Text } }))
Note that on the ListView the Sorting property needs to be set to None otherwise the items will be added back alphabetically.
coller event
$MyListView.add_ColumnClick({SortListView $this $_.Column})
Sort function whit memorize the lastest Order :
function SortListView {
Param(
[System.Windows.Forms.ListView]$sender,
$column
)
$temp = $sender.Items | Foreach-Object { $_ }
$Script:SortingDescending = !$Script:SortingDescending
$sender.Items.Clear()
$sender.ShowGroups = $false
$sender.Sorting = 'none'
$sender.Items.AddRange(($temp | Sort-Object -Descending:$script:SortingDescending -Property #{ Expression={ $_.SubItems[$column].Text } }))
}

Return one of many variables from a function

I'm trying to figure out how to simplify this process, but it's not as simple as I thought.
I have a config file that looks similar to this:
[string][1][options]
$List = #(
"c:\path\to\file,1,-a,-b,-c,-d,-e"
)
The only items required are the [string] and the [1]. There are 10 options (-a, -b etc), potentially more.
Each of which is optional and could be supplied in any order.
In the main script I then do the following at present:
foreach ($a in $List) {
$dataSplit = $a -split"(,)"
$string = $dataSplit[0]
$number = $dataSplit[2]
$ds4 = $dataSplit[4]
if(!$ds4) {
$ds4 = "0"
} elseif($ds4.StartsWith("-a")) {
$a_set = 1
write-host "a_set has been set to $a_set"
} elseif($ds4.StartsWith("-b")) {
$b_set = 1
write-host "b_set has been set to $b_set"
}
. . .
if(!$ds5) {
$ds5 = "0"
}
. . .
As you can imagine this gets quite long. So I thought I would simplify it with a function. e.g.
function get-additional($item) {
if($item.StartsWith("-a")) {
$a_set = 1
Write-Host "$a_set has been set"
return $a_set
}
if($item.StartsWith("-b")) {
$b_set = 1
Write-Host "$b_set has been set"
return $b_set
}
}
And then call it thus:
if(!$ds4) {
$ds4 = "0"
} else {
get-additional($ds4)
}
Is there a way to do this? I've seen pleanty of examples if you only have a single variable to return, or even a fixed number, but none that allow for the return of 'one of many' variables.
Here is the (shortened) script in one if it helps:
$List = #(
"c:\path\to\file,1,-b,-c,-d,-e"
)
function get-additional($item) {
if($item.StartsWith("-a")) {
$a_set = 1
Write-Host "a_set has been set to $a_set"
return $a_set
}
if($item.StartsWith("-b")) {
$b_set = 1
Write-Host "b_set has been set to $b_set"
return $b_set
}
}
$a_set = 0
$b_set = 0
$c_set = 0
foreach ($a in $List) {
$dataSplit = $a -split"(,)"
$string = $dataSplit[0]
$number = $dataSplit[2]
$ds4 = $dataSplit[4]
Write-Host "ds4 = $ds4"
if(!$ds4) {
$ds4 = "0"
} else {
get-additional($ds4)
}
$ds5 = $dataSplit[6]
Write-Host "ds5 = $ds5"
if(!$ds5) {
$ds5 = "0"
} else {
get-additional($ds5)
}
}
Write-Host "a = $a_set"
Write-Host "b = $b_set"
The desired result at the end would be
a = 0
b = 1
- - - UPDATE 2015-11-30 16:54
In case it helps to understand what I am going for here's a Sample from my actual script
$cfg_AppList = #(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0"
"C:\Path\to\application3\app3.exe instance3,1,-p78901"
)
function get-additional($item)
{
$script:pval = "0"
if($item.StartsWith("-p"))
{
$script:pval = $ds4.substring(2)
write-host "$pval is a pval"
}
}
$AppObject = #()
foreach($a in $cfg_AppList)
{
$dataSplit = $a -split","
$AppVal = $dataSplit[0]
$checkVal = $dataSplit[1]
$ds4 = $dataSplit[2]
if(!$ds4)
{
$ds4 = "0"
}
else
{
get-additional($ds4)
}
$AppObject += New-Object PSObject -property #{
AppVal = "$AppVal";
checkVal = "$checkVal";
pval = "$pval";
}
}
The $AppObject object is then referenced and updated as the script progresses.
The values supplied in pval and (see below eval) will determine what happens.
I now need to add a second element -e which will be included thus:
$cfg_AppList = #(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0,-e"
"C:\Path\to\application3\app3.exe instance3,1,-e,-p78901"
)
It will be either selected 1 or not selected 0, and added to the $AppObject Array as eval=$eval (1|0).
Going forward I have more options I plan to introduce, hence the need to find the most efficient way to handle them all.
- - - UPDATE 2015-12-01 11:39
OK, What I have gone with is a combination of both ideas below.
Placing the options into an array and looping through them, then using a SWITCH statement to see which ones are set.
$AppObject = #()
foreach($a in $cfg_AppList)
{
$pval = 0
$eval = 0
$AppVal,$CheckVal,$options = $a -split","
foreach($opt in $options)
{
switch -wildcard ($opt)
{
'-p*' { $pval = $opt.substring(2) }
'-e' { $eval = 1 }
}
}
$AppObject += New-Object PSObject -property #{
AppVal = "$AppVal";
CheckVal = "$CheckVal";
pval = "$pval";
eval = "$eval";
}
}
First off, don't capture the , in your split operation if you're not planning to use it for anything, just use -split "," (no parentheses).
We can make use of multiple variable assignment to "shift" away to string and number 1:
$s,$n,$opts = "string,1,-a,-b,-c" -split ","
$opts will now contain the string array: #("-a","-b","-c")
The easiest way to check for whether a predetermined set of options is present or not, is to simply loop through all possible options and see if they are contained in the input string:
function Parse-InputString
{
param($InputString)
# prepare the options you want to check for
$PossibleOptions = "abcde".ToCharArray()
# Split the input string
$String,$Number,$Options = $InputString -split ","
# Create a new object with the string and number values
$OutputObject = New-Object psobject -Property #{
"String" = $String
"Number" = $Number
}
# Now inspect the $Options array to see if any of them are set
foreach($PossibleOption in $PossibleOptions){
$OptionSet = if($Options -contains "-$PossibleOption"){
1
} else {
0
}
# Add the information to the object
$OutputObject |Add-Member -MemberType NoteProperty -Name $PossibleOption -Value $OptionSet
}
# return the object carrying all the information
return $OutputObject
}
Now you can have your input string parsed nicely into an actual object:
PS C:\> Parse-InputString -InputString "c:\path\to\file,1,-b,-c,-d,-e"
Number : 1
String : c:\path\to\file
a : 0
b : 1
c : 1
d : 1
e : 1
The easiest way would be to update the global variables in your function without returning anything:
function Get-Additional($item) {
if ($item.StartsWith("-a")) {
$global:a_set = 1
Write-Host "a_set has been set to $a_set"
}
if ($item.StartsWith("-b")) {
$global:b_set = 1
Write-Host "b_set has been set to $b_set"
}
}
However, modifying global variables in functions is not a good practice, because it's difficult to debug. I wouldn't recommend going this route.
A better approach is to pass your current values as parameters into the function, return the modified values, and assign them back to variables.
function Get-Additional($item, $a, $b) {
if ($item.StartsWith("-a")) {
$a = 1
Write-Host "a_set has been set to $a_set"
}
if ($item.StartsWith("-b")) {
$b = 1
Write-Host "b_set has been set to $b_set"
}
#($a, $b)
}
$set_a, $set_b = Get-Additional $ds4 $set_a $set_b
In the above sample the function returns a list of the modified values (#($a, $b)), which are then assigned back to the list $set_a, $set_b. The return keyword is not required for returning something from a PowerShell function. It controls only where to return from a function, not what to return.
With that said, for your scenario I wouldn't use a function in the first place. A switch statement would be better suited for this kind of manipulation:
switch -wildcard ($ds4) {
'-a*' { $set_a = 1 }
'-b*' { $set_b = 1 }
}

Resources