PowerShell get out result from a function - powershell

I have the following code that I'm trying to return the string Good as an out parameter from FooFunc.
How can I do that?
function FooFunc($a, [ref]$result){
if (4 -gt 1) {
$result = "Good"
return $true
} else {
$result = "Bad"
return $false
}
}
try
{
FooFunc "Bar" ([ref]$result)
Write-Host $result
}
catch
{
Write-Host $_.Exception.Message
}
EDIT I don't want to return the answer with return, I need it as ref or out parameters

You can try
Function Foo{
if(4 -gt 1)
{
return "Good"
}
else
{
return "Bad"
}
}
try{
$result = Foo
Write-Output $result
}
catch{
Write-Output "Some message"
}

[ref] is for passing variable into a function so it needs to exist in your current scope which is why you are getting the error. I think you are misunderstanding the usage. Try this:
function FooFunc([ref]$result){
if (4 -gt 1) {
$result = 'good' + $result.value
$result
} else {
$result = "Bad"
return $false
}
}
try
{
$result = 'badval'
FooFunc ([ref]$result)
}
catch
{
Write-Host $_.Exception.Message
}
If you pipe the function to get-member you will see it returns ref and the output is using the variable you are passing in.

Applying [ref] to a parameter will result in a PSReference object being passed to the function.
In the case of function FooFunc($a, [ref]$result), using $result = "Good" will change the local $result variable in the function from a PSReference to a string, but will have no effect on the variable the caller passed in. To modify the outer variable, you would want to set $result.Value = "Good".
And as Noah Sparks mentioned, on the caller side you also need to declare the variable before using it as argument.
function FooFunc( $a, [ref]$result ) {
if( 4 -gt 1 ) {
$result.Value = 'Good' # change the reference value, not the variable
return $true
}
else {
$result.Value = 'Bad'
return $false
}
}
try {
$result = $null # declare variable before using it for a reference
FooFunc 'Bar' ([ref]$result)
Write-Host $result
}
catch {
Write-Host $_.Exception.Message
}
Another option would be to output multiple values from your function:
function FooFunc( $a ) {
if( 4 -gt 1 ) {
# Output both result and success values
'Good'
return $true
}
else {
# This single return is the same as the output and return above
return 'Bad', $false
}
}
try {
$result,$success = FooFunc 'Bar' # Receive both values
$success # Implicitly output success value, as before
Write-Host $result
}
catch {
Write-Host $_.Exception.Message
}

Related

Powershell class first instance misbehaving

I am getting my feet wet with PS classes, by trying to implement a path type identifier class. Basically I want to pass a string in and identify variations on file system and registry paths. Mostly it is working, but oddly the first item I test, no matter what it is, the properties are blank when I look, though no error shows up. And it seems like the results are actually from the previous instance, at least for the "successful" instances. Thrown exceptions seem to be working as expected. So, when $test = #('C:\folder', 'D:\', 'C:', 'K:', '\\Server\Folder\*', '\junk', '?:'), my results will be
C:\folder
D:\
FileSystem_Folder
LocalDisk folder
C:
FileSystem_Drive
OpticalDisk drive
K:
FileSystem_Drive
LocalDisk drive
\\Server\Folder\*
FileSystem_Drive
Unknown drive
! \junk
Not Identified: \junk
! ?:
Not Identified: ?:
This is the code in question.
class PxPath {
# Properties
hidden [array]$validPathTypes = #('FileSystem_Drive', 'FileSystem_Folder', 'FileSystem_File', 'Registry_Key', 'Registry_Property')
hidden [hashtable]$regExLookup = #{
registryHive = '^(?<hive>HKCC|HKEY_CURRENT_CONFIG|HKCR|HKEY_CLASSES_ROOT|HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE)(?<seperator>:)?$'
registryPath = '^(?<hive>HKCC|HKEY_CURRENT_CONFIG|HKCR|HKEY_CLASSES_ROOT|HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE)(?<seperator>:)?\\(?<remainder>.+)$'
fileSystemDriveOnly= '^(?<drive>[a-zA-Z]:)(\\)?$'
fileSystemDrivePath = '^(?<drive>[a-zA-Z]:)\\(?<remainder>.+)$'
fileSystemUNCPath = '^(?<server>\\\\[^<>:"/\\\|]*)\\(?<remainder>.+)$'
}
hidden [hashtable]$driveTypeLookup = #{
0 = 'Unknown'
1 = 'NoRootDirectory'
2 = 'RemovableDisk'
3 = 'LocalDisk'
4 = 'NetworkDrive'
5 = 'OpticalDisk'
6 = 'RamDisk'
}
[string]$Description = $null
[string]$Type = $null
# Constructors
PxPath ([string]$path) {
$this.PathType($path)
}
PxPath ([string]$path, [string]$pathType) {
if ($this.validPathTypes -contains $pathType) {
$this.PathType($path)
if ($this.Type -ne $pathType) {
Throw "$($this.Type) does not match: $pathType"
}
} else {
Throw "Not a valid path type: $pathType"
}
}
hidden [void] PathType ([string]$path) {
[string]$driveType = $null
[string]$extension = $null
[string]$extension = $null
switch -regex ($path) {
$this.regExLookup.fileSystemDriveOnly {
$driveType = $this.driveTypeLookup.Get_Item([int](Get-WmiObject Win32_LogicalDisk -computerName:. -filter:"name='$($matches.drive)'" | Select DriveType).DriveType)
$this.Description = "$driveType drive"
$this.Type = 'FileSystem_Drive'
}
$this.regExLookup.fileSystemDrivePath {
$driveType = $this.driveTypeLookup.Get_Item([int](Get-WmiObject Win32_LogicalDisk -computerName:. -filter:"name='$($matches.drive)'" | Select DriveType).DriveType)
if ($extension = [System.IO.Path]::GetExtension($path)) {
$this.Description = "$driveType file"
$this.Type = 'FileSystem_File'
} else {
$this.Description = "$driveType folder"
$this.Type = 'FileSystem_Folder'
}
}
$this.regExLookup.fileSystemUNCPath {
if ($extension = [System.IO.Path]::GetExtension($path)) {
$this.Description = "UNC file"
$this.Type = 'FileSystem_File'
} else {
$this.Description = "UNC folder"
$this.Type = 'FileSystem_Folder'
}
}
$this.regExLookup.registryPath {
}
default {
Throw "Not Identified: $path"
}
}
}
}
CLS
$test = #('C:\folder', 'D:\', 'C:', 'K:', '\\Server\Folder\*', '\junk', '?:')
foreach ($testitem in $test) {
$path = try {
[PxPath]::New($testitem)
Write-Host $testitem
Write-Host " $($path.Type)"
Write-Host " $($path.Description)"
Write-Host
} catch {
Write-Host "! $testitem"
Write-Host " $($_.Exception.Message)"
Write-Host
}
}
I suspect the issue is something I just don't understand with classes, but I guess a bug in PowerShell that I need to work around could also be the culprit.

Powershell logging to lost network location: FileStream won't reconnect/flush

I am working on a script logging solution for an install script that has many tasks and long processing times, and I am trying to address the issue of network dropout. I am also moving to a StreamWriter based approach vs my old Add-Content approach for performance reasons.
The problem I am having is that once the network drops out, the StreamWriter doesn't reconnect. So, first question is, CAN I reconnect, or is this a limitation of StreamWriter? The fact that the StreamWriter has a cache that can be Flushed makes me think I may be doing a bunch of work to recreate functionality that is already there.
And second, I am starting to think a simpler/better solution is simply to write the log to a local folder, so the log is always complete, then simply attempt to copy that to the network location for progress review. I had been thinking about implementing parallel logs, so a loss of the network would still leave the local copy complete. Curious if anyone else looks at this and says "Well, YEAH, dufus, obviously."
function Get-PxLogFile {
return $script:pxLogFile
}
function Set-PxLogFile {
param (
[string]$path
)
if (Test-Path $path) {
Remove-Item $path -force
}
[string]$script:pxLogFile= $path
}
function Get-PxLogWriter {
$logFile = Get-PxLogFile
if (-not $script:pxFileStream) {
$script:pxFileStream = New-Object io.fileStream $logFile, 'Append', 'Write', 'Read'
$script:pxlogWriter = New-Object io.streamWriter $script:pxFileStream
} elseif ($script:pxFileStream.name -ne $logFile) {
$script:pxFileStream = New-Object io.fileStream $logFile, 'Append', 'Write', 'Read'
$script:pxlogWriter = New-Object io.streamWriter $script:pxFileStream
}
return $script:pxlogWriter
}
function Dispose-PxLogFile {
$script:pxlogWriter.Dispose()
$script:pxFileStream.Dispose()
$script:pxFileStream = $null
$script:pxlogWriter = $null
}
# Shared
function Get-PxDeferredLog {
return ,$script:deferredLog # , keeps PS from unrolling a single item array into a string
}
function Set-PxDeferredLog {
param (
[collections.arrayList]$deferredLog
)
[collections.arrayList]$script:deferredLog = $deferredLog
}
function Get-PxDeferredLogTimes {
return ($script:deferredLogTimes -join ', ')
}
function Finalize-PxLogFile {
$logWriter = Get-PxLogWriter
if (($deferredLog = Get-PxDeferredLog).count -gt 0) {
$abandonTime = (Get-Date) + (New-TimeSpan -seconds:3)
$logWriter = Get-PxLogWriter
:lastChanceLogWindow do {
$deferredLog = Get-PxDeferredLog
:deferredItemsWrite do {
try {
$logWriter.WriteLine($deferredLog[0])
if ($deferredLog.count -gt 0) {
$deferredLog.RemoveAt(0)
} else {
break :lastChanceWindow
}
} catch {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
Set-PxDeferredLog $deferredLog
if ($deferredLog.count -eq 0) {
break :lastChanceWindow
}
if ((Get-Date) -gt $abandonTime) {
break :lastChanceLogWindow
}
} while ((Get-Date) -lt $abandonTime)
if ($deferredLog) {
Write-Host "Failed to write all log entries"
}
}
$script:deferredLogItems = $script:deferredLogTimes = $null
}
function Add-PxLogFileContent {
param (
[string]$string
)
$logWriter = Get-PxLogWriter
# Nested Functions
function Add-PxDeferredLogItem {
param (
[string]$item
)
if (-not $script:deferredLog) {
[collections.arrayList]$script:deferredLog = New-Object collections.arrayList
}
if ($script:deferredLog.count -eq 0) {
Start-PxDeferredLogTime
}
[void]$script:deferredLog.Add($item)
}
function Start-PxDeferredLogTime {
if (-not $script:deferredLogTimes) {
[collections.arrayList]$script:deferredLogTimes = New-Object collections.arrayList
}
if ((-not $script:deferredLogTimes) -or (-not $script:deferredLogTimes[-1].EndsWith('-'))) {
[void]$script:deferredLogTimes.Add("$((Get-Date).ToString('T'))-")
}
}
function Stop-PxDeferredLogTime {
if ($script:deferredLogTimes[-1].EndsWith('-')) {
$script:deferredLogTimes[-1] = "$($script:deferredLogTimes[-1])$((Get-Date).ToString('T'))"
}
}
$deferredLogProcessed = $false
if ([collections.arrayList]$deferredLog = Get-PxDeferredLog) {
$deferredLogProcessed = $true
:deferredItemsWrite do {
try {
$logWriter.WriteLine($deferredLog[0])
$logWriter.Flush()
$deferredLog.RemoveAt(0)
} catch {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
if ($deferredLog.count -eq 0) {
$deferredLogPending = $false
} else {
$deferredLogPending = $true
}
Set-PxDeferredLog $deferredLog
} else {
$deferredLogPending = $false
}
if (-not $deferredLogPending) {
try {
if ($logWriter.WriteLine($string)) {
$logWriter.Flush()
}
if ($deferredLogProcessed) {Stop-PxDeferredLogTime}
} catch {
Add-PxDeferredLogItem $string
Write-Host "Failed: $(Get-Date)`n$($_.Exception.Message)"
}
} else {
Add-PxDeferredLogItem $string
}
}
### MAIN
Clear-Host
$script:deferredLogItems = $script:deferredLogTimes = $null
$logPath = '\\px\Content'
Write-Host 'logTest1.txt'
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:5)
#Set-PxLogFile "$([System.IO.Path]::GetFullPath($env:TEMP))\logTest1.txt"
Set-PxLogFile "$logPath\logTest1.txt"
do {
Add-PxLogFileContent "logged: $(Get-Date)"
Start-SLeep -s:10
} while ((Get-Date) -lt $endTime)
Finalize-PxLogFile
Write-Host 'logTest2.txt'
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:5)
#Set-PxLogFile "$([System.IO.Path]::GetFullPath($env:TEMP))\logTest2.txt"
Set-PxLogFile "$logPath\logTest2.txt"
do {
Add-PxLogFileContent "logged: $(Get-Date)"
Start-SLeep -s:10
} while ((Get-Date) -lt $endTime)
if ([string]$deferredLogTimes = Get-PxDeferredLogTimes) {
Add-PxLogFileContent "Deferred logging time ranges: $deferredLogTimes"
}
Finalize-PxLogFile
Dispose-PxLogFile

collections.ArrayList Add method producing a string on first Add

I am attempting to mock up a data structure and logic to handle deferred log writing, and I am running into an odd situation. if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) { errors when the returned arrayList is only one item, with
Cannot convert the "logged: 07/20/2019 10:56:29" value of type "System.String" to type "System.Collections.ArrayList".
Once there are two items it's fine. Now I know an array of a single item needs to be forced to be an array because PS is sometimes too clever for it's own good, but I thought the Add method of a [collections.arrayList] produced an arrayList even on the first Add. AM I totally misunderstanding, or is my logic wrong somewhere else?
I also tried casting the results of the function call like this
if ([collections.arrayList]$deferredLog = [collections.arrayList](Get-PxDeferredLogItems)) { but that is even worse, it errors at every call. I also tried initializing the variable to an empty arrayList with Set-PxDeferredLogItems (New-Object collections.arrayList) in Main, rather the n initializing it on the first call of Add-PxDeferredLogItem, but that shows the same behavior, errors until the deferred log has two items. I verified this by changing the Write-Host directly after the erroring line to Write-Host "Process deferred items $($deferredLog.count)", and I get errors till it shows a 2, then everything works as expected.
function Get-PxDeferredLogItems {
return $script:deferredLogItems
}
function Set-PxDeferredLogItems {
param (
[collections.arrayList]$deferredLog
)
[collections.arrayList]$script:deferredLogItems = $deferredLog
}
function Add-PxDeferredLogItem {
param (
[string]$item
)
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add($item)
}
function Add-PxLogContent {
param (
[string]$string
)
function Assert-PxWrite {
if ((Get-Random -minimum:1 -maximum:4) -gt 1) {
return $true
} else {
return $false
}
}
if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) {
Write-Host "Process deferred items"
:deferredItemsWrite do {
if (Assert-PxWrite) {
Write-Host "$($deferredLog[0]) ($(Get-Date))"
$deferredLog.RemoveAt(0)
} else {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
if ($deferredLog.count -eq 0) {
$deferredLogPending = $false
} else {
$deferredLogPending = $true
}
Set-PxDeferredLogItems $deferredLog
} else {
$deferredLogPending = $false
}
if (-not $deferredLogPending) {
if (Assert-PxWrite) {
Write-Host $string
} else {
Add-PxDeferredLogItem $string
}
} else {
Add-PxDeferredLogItem $string
}
}
### MAIN
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:2)
Clear-Host
do {
Add-PxLogContent "logged: $(Get-Date)"
Start-SLeep -s:5
} while ((Get-Date) -lt $endTime)
if ($deferredLog = Get-PxDeferredLogItems) {
foreach ($item in $deferredLog) {
Write-Host "!$item"
}
}
EDIT: It seems to be related to passing the arrayList, as this works as expected when there are no functions involved.
Clear-Host
$deferredLogItems = New-Object collections.arrayList
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('One')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Two')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Three')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
function Get-DeferredLogItems {
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add("$($script:deferredLogItems.count + 1)")
return $script:deferredLogItems
}
$script:deferredLogItems = $localDeferredLogItems = $null
foreach ($i in 1..5) {
[collections.arrayList]$localDeferredLogItems = Get-DeferredLogItems
Write-Host "$($localDeferredLogItems.getType()) $($localDeferredLogItems.count) $localDeferredLogItems"
}
EDIT2: Replaced the line # reference with the actual offending line of code, because Confusion.
EDIT3: For what it's worth, forcing the results of Get-DeferredLogItems into an array DOES work. But I still wonder why that's even needed. Why does the Add method produce a correct arrayList at first add but this gets #%#$ed up by PowerShell when passed as a return value?

Application runs in Windows 7 but not in Windows 10

I have created an app for back up and restore of computers. I also allows modification of ADObjects through the use of custom Profile.ps1 file. The app runs fine in the ISE with no errors and works properly no errors in Windows 7. However, when I try to run it in a newly imaged Windows 10 machine I get "Property Can Not Be Found" errors on all my object properties.
The thing is I can read all the properties when I fill comboboxes fine. The error only occurs when the form is submitted. I will attach 1 of the forms I am having a problem with. Again it runs fine in Windows 7, but not Windows 10.
Could this be a problem with Microsoft updates?
Also, yes, I am setting Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted.
Error message:
The property 'company' cannot be found on this object. Verify that the
property exist and can be set.
+ $CO.company = $company
+ Categoryinfo :InvalidOperation: (:) [] RuntimeExeption
Code:
. \\iotsdsp01pw\installs$\MoveToOU\PcDeployment\Profile.ps1
#region Validation Functions
function Validate-IsEmail ([string]$Email) {
return $Email -match "^(?("")("".+?""#)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$"
}
function Validate-IsURL ([string]$Url) {
if ($Url -eq $null) {
return $false
}
return $Url -match "^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$"
}
function Validate-IsName ([string]$Name, [int]$MaxLength) {
if ($MaxLength -eq $null -or $MaxLength -le 0) {
#Set default length to 40
$MaxLength = 40
}
return $Name -match "^[a-zA-Z''-'\s]{1,$MaxLength}$"
}
function Validate-IsIP ([string]$IP) {
return $IP -match "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
}
function Validate-IsEmptyTrim ([string]$Text) {
if ($text -eq $null -or $text.Trim().Length -eq 0) {
return $true
}
return $false
}
function Validate-IsEmpty ([string]$Text) {
return [string]::IsNullOrEmpty($Text)
}
function Validate-IsDate ([string]$Date) {
return [DateTime]::TryParse($Date, [ref](New-Object System.DateTime))
}
#endregion
$No_Load = {
$NewForm.Close()
#Initialize variables
$dateTime = Get-Date -Format G
$userName = (Get-WmiObject -Class Win32_ComputerSystem -Property UserName).UserName
$computerName = $env:computername
#Varables for display
$distinguishedName = (Get-dn computer cn $computerName)
$computerObject = (Get-ADObject $distinguishedName)
$organizationalUnit = (Get-ADObject "OU=Agencies, DC=state, DC=in, DC=us")
#Initialize Form Controls
$lblUserNameNewNo.Text = $userName
$lblComputerNameNewNo.Text = $computerName
$lblPhysicalLocationNewNo.Text = $computerObject.location
$txtBillingCodeNewNo.Text = $computerObject.departmentNumber
$comboboxAccountTypeNewNo.Text = $computerObject.extensionAttribute15
$comboboxOrganizationalUnitNewNo.Text = $computerObject.company
Load-ComboBox -ComboBox $comboboxOrganizationalUnitNewNo ($organizationalUnit.children | %{ $_.OU })
}
#region Control Helper Functions
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 [Array]) {
$ComboBox.BeginUpdate()
foreach ($obj in $Items) {
$ComboBox.Items.Add($obj)
}
$ComboBox.EndUpdate()
} else {
$ComboBox.Items.Add($Items)
}
$ComboBox.DisplayMember = $DisplayMember
}
#Validation
function ParameterValidate {
Param (
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[ValidateLength(1, 10)]
[String]$Text
)
return $true
}
$comboboxOrganizationalUnitNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $comboboxOrganizationalUnitNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "Please select agency.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "");
}
}
$txtBillingCodeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $txtBillingCodeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($txtBillingCodeNewNo, "Please enter billing code.");
} else {
#Clear the error message
$errorprovider1.SetError($txtBillingCodeNewNo, "");
}
}
$comboboxAccountTypeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
$result = Validate-IsEmptyTrim $comboboxAccountTypeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "Please enter agency type.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "");
}
}
$control_Validated = {
#Pass the calling control and clear error message
$errorprovider1.SetError($this, "");
}
$No_FormClosing = [System.Windows.Forms.FormClosingEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosingEventArgs]
#Validate only on OK Button
if ($No.DialogResult -eq "OK") {
#Init the Validation Failed Variable
$script:ValidationFailed = $false
#Validate the Child Control and Cancel if any fail
$No.ValidateChildren()
#Cancel if Validation Failed
$_.Cancel = $script:ValidationFailed
}
}
#Events
$buttonColor_Click = {
#TODO: Place custom script here
$colordialog1.ShowDialog()
}
$linklblViewListNewNo_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{
Start-Process "http://billingcodes.iot/"
}
$btnSubmitNewNo_Click = {
#TODO: Place custom script here
$company = $comboboxOrganizationalUnitNewNo.Text
$departmentNumber = $txtBillingCodeNewNo.Text
$accountType = $comboboxAccountTypeNewNo.Text
if ($accountType -eq "Seat") {
$accountType = " "
}
#Varables for Set-ADObject
$computerObject.company = $company
$computerObject.departmentNumber = $departmentNumber
$computerObject.extensionAttribute15 = $accountType
try {
$computerObject.SetInfo()
[Environment]::Exit(1)
} catch {
$labelDialogRedNewNo.Text = "AD computer object not found"
}
}
This is your culprit (from what I can see):
$No_Load = {
...
$computerObject = (Get-ADObject $distinguishedName)
...
}
...
$btnSubmitNewNo_Click = {
...
$computerObject.company = $company
...
}
You assign a computer object to the variable $computerObject in one scriptblock, and try to change one of its properties in another scriptblock. However, to be able to share a variable between scriptblocks you need to make it a global variable, otherwise you have two separate local variables that know nothing about each other.
$No_Load = {
...
$global:computerObject = Get-ADObject $distinguishedName
...
}
...
$btnSubmitNewNo_Click = {
...
$global:computerObject.company = $company
...
}
BTW, I doubt that this ever worked in Windows 7, since it failed with the same error on my Windows 7 test box.

Change the evaluator within a switch statement

Is there a way, in a 'switch' to modify the variable you are evaluating on, and get it to change the match?
$var = "a"
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"; $var = "b" ; continue}
"b" {Write-Host "2st match for 'b'"}
}
I would love to be able to get the above to match:
1st match for 'a'
2nd match for 'a'
2nd match for 'b'
$Destination = "vmstores:\vcsa#443\Training\Local-B\David" #is a param of my function, which could be a bunch of different types of object.
$DestinationType = $Destination.GetType().Name
switch ($DestinationType){
"String" {
if ((Test-Path $Destination) -eq $true){
if ((Get-Item $Destination).GetType().Name -eq "DatastoreFolderImpl"){
$DestinationType = "DatastoreFolderImpl"
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
}
elseif ((Get-Item $Destination).GetType().Name -eq "DatastoreFileImpl"){
$DestinationType = "DatastoreFileImpl"
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
}
; continue
}
"DirectoryInfo" {
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"FileInfo" {
if ((Test-Path $Destination) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"DatastoreFileImpl" {
if ((Test-Path $Destination) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"DatastoreFolderImpl" {
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"NasDatastoreImpl" {
New-PSDrive -Name "DestMount" -Root \ -PSProvider VimDatastore -Datastore $Destination | Out-Null
if ((Test-Path ("DestMount:").insert(10,"\").Insert(11,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
$Destination = ("DestMount:").insert(10,"\")
; break
}
"VMFSDatastoreImpl" {
New-PSDrive -Name "DestMount" -Root \ -PSProvider VimDatastore -Datastore $Destination | Out-Null
if ((Test-Path ("DestMount:").insert(10,"\").Insert(11,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
$Destination = ("DestMount:").insert(10,"\")
; break
}
}
As you can see, it would be more elegant if i could update the $DestinationType so i could re-use the statement in the other switch blocks, rather than the extra 'ifs'
While I don't think you can change the switch statements variable mid check, you can do nested switch statements. This code gives your requested output:
$var = "a"
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"
$var = "b"
switch ($var) {
"b" {Write-Host "2st match for 'b'"}
} #End sub-switch
} #End "A" check
} #End Primary switch
Now, I'm not sure what your overall goal is, there might be better ways to do this with functions and such.
Edit after updated question and comments:
From looking at the updated code, you can pull the one if statement that sets $DestinationExists = $true. You might be able to reorganize your if statements so they only show up once. Unfortunately, there is no way to get the switch statement to change the variable mid-switch. From your comment, maybe you can add an extra parameter that will dictate the type, so you can us one large switch statement based off the type. Something like this:
switch ($DataType) {
"String" {<#Things to do if string#>}
"OtherTypes" {<#Continue like this#>}
}#End Switch ($DataType)
I think at that point though, I would start using Parameter Sets. Here is a blog describing about Parameter Sets
Here is a somewhat convoluted way to do it. Note that the example code is similar but not the same as the code in OP.
add-type #"
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation;
public class PSVarEnumeration : IEnumerable
{
private ScriptBlock _getterScript;
public PSVarEnumeration(ScriptBlock getterScript)
{
_getterScript = getterScript;
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator) GetEnumerator();
}
public PSVarEnumerator GetEnumerator()
{
return new PSVarEnumerator(_getterScript);
}
}
public class PSVarEnumerator : IEnumerator
{
private ScriptBlock _getterScript;
bool areDone = false;
// position isn't used for anything
int position = -1;
public PSVarEnumerator(ScriptBlock getterScript)
{
_getterScript = getterScript;
}
public bool MoveNext()
{
if (!areDone) {
if (Current != null) {
position++;
return true;
} else {
areDone = true;
return false;
}
} else {
return false;
}
}
public void Reset()
{
position = -1;
areDone = false;
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public Object Current
{
get
{
Collection<PSObject> results = _getterScript.Invoke();
return results[0];
}
}
}
"#
$mySwitcher ="a"
$mySwitcherScript = {$mySwitcher}
$var = new PSVarEnumeration ( $mySwitcherScript)
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"; $mySwitcher = "b" ; continue}
"b" {Write-Host "2nd match for 'b'"; $mySwitcher = $null}
}
<#
Output I get is:
1st match for 'a'
2nd match for 'a'
1st match for 'b'
2nd match for 'b'
#>