Null runtime exception when using IsNullOrEmpty - powershell

I am trying to init a config object with string value or file content value (if string variable is null or empty).
$ConfigObj = New-Object PSObject -Property #{
"Name" = $env:OOO_NAME
"Content"=
if([string]::IsNullOrEmpty($env:NOTE) -and [string]::IsNullOrEmpty($env:PUBLISH_FILEPATH))
{
Write-Host "Check input"
exit 1
}
elseif(![string]::IsNullOrEmpty($env:PUBLISH_NOTE))
{
$env:PUBLISH_NOTE ############# **it throws exception here.**
}
else
{
Get-Content ($env:PUBLISH_FILEPATH)
}
}
It works fine when this script execute by itself, but it yells RuntimeException after executing several PowerShell scripts. The following is exception info:
You cannot call a method on a null-valued expression.
+ $env:PUBLISH_NOTE
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
It really doesn't make any sense to me, since I use $env:{environment variable name} to access system environment variables and why doesn't it yell at earier lines such as
elseif(![string]::IsNullOrEmpty($env:PUBLISH_NOTE))
Please kindly explain what's the root case of this runtime exception.

Related

runspace: EndInvoke() fails to return all the scriptblocks output , only the last exception

The script block
$sb = {
write-output "Testing 1"
write-output "Testing 2"
throw " Exception in sb"
}
Calling EndInvoke() only returns the below. My runspace tasks are in some case hours long. I can not lose all the output except the last exception. I do not have control over the script blocks as they get passed into my cmdlets.
How do I resolve that?
Exception calling "EndInvoke" with "1" argument(s): " Exception in sb"
At line:1 char:1
+ $j.PowerShell.EndInvoke($j.Job)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : RuntimeException
By the time you call EndInvoke(), you are already too late. There is no way to receive the data from the output stream as all that data is discarded and the only thing you will get is the thrown exception message. Instead, you have to change how you do your initial BeginInvoke() call to allow you to capture the output.
Using an overloaded BeginInvoke(TInput,TOutput) call, you can both pass your input commands (if needed, or blank), as well as you have to supply a buffer for the output to be stored (of type System.Management.Automation.PSDataCollection[psobject]). So your code looks like this:
$sb = {
write-output "Testing 1"
write-output "Testing 2"
throw " Exception in sb"
}
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript($sb)
$InputObject = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$OutputObject = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$Handle = $PowerShell.BeginInvoke($InputObject,$OutputObject)
Calling EndInvoke() will give you the error message:
PS C:\> $PowerShell.EndInvoke($Handle)
Exception calling "EndInvoke" with "1" argument(s): " Exception in sb"
At line:1 char:1
+ $PowerShell.EndInvoke($Handle)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : RuntimeException
But the output is stored in the $OutputObject buffer:
PS C:\> $InputObject
PS C:\> $OutputObject
Testing 1
Testing 2

Open Serial Port with variable

I want to open a COM port in my powershell script:
function openComPort($number, $baud) {
$port = New-Object System.IO.Ports.SerialPort("COM$number", $baud, "None", 8, "One")
$port.Open()
return $port
}
$myOpenedPort = openComPort(1, 9600)
This fails with
New-Object : Exception calling ".ctor" with "5" argument(s): "Positive number required.
Parameter name: BaudRate"
At line:9 char:20
+ $port = New-Object <<<< System.IO.Ports.SerialPort("COM$number", $baud, "None", 8, "One")
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At line:11 char:12
+ $port.Open <<<< ()
+ CategoryInfo : InvalidOperation: (Open:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
What am I doing wrong?
You're calling your function wrong, Powershell (unlike other languages) doesn't require brackets when calling a function, see about_functions for further info.
Using them like that groups everything inside the brackets into a single item, this is being sent to the first parameter $number, leaving $baud empty - which is causing your errors.
The correct syntax is:
openComPort 1 9600
EDIT: It's also good practice to have your params into a param() block (this is a step towards using advanced functions.
And to also set a param type to ensure you receive the correct input type. They are both int in this case - as you only want a positive whole number.
This would update your function to:
function openComPort {
Param(
[int]$number,
[int]$baud
)
$port = New-Object System.IO.Ports.SerialPort("COM$number", $baud, 'None', 8, 'One')
$port.Open()
return $port
}
$myOpenedPort = openComPort -number 1 -baud 9600

Remove a PowerShell session variable?

I'm attempting to set and clear a session variable in a PowerShell module:
function Set-Variable
{
[CmdletBinding()]
param(
[string]$Value = $PSCmdlet.SessionState.PSVariable.Get('Value').Value
)
# if `Value` not supplied on the command line and not available in the session, prompt for it
if (!$PSCmdlet.SessionState.PSVariable.Get('Value') -And !$Value) {
$Value = Read-Host "Value"
}
# if `Value` has been supplied on the command line, save it in the session
if ($Value) {
$PSCmdlet.SessionState.PSVariable.Set('Value',$Value)
}
}
Export-ModuleMember Set-Variable
function Remove-Variable {
$PSCmdlet.SessionState.PSVariable.Remove('Value')
# also throws an exeception
# $PSCmdlet.SessionState.PSVariable.Set('Value',$null)
}
Export-ModuleMember Remove-Variable
Setting the value works as expect, however, removing the variable or setting its value to null produces an error:
PS> Remove-Variable
You cannot call a method on a null-valued expression.At
C:\Users\XXXX\Documents\WindowsPowerShell\Modules\foo\foo.psm1:39 char:5
+ $PSCmdlet.SessionState.PSVariable.Remove('Value')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
and:
You cannot call a method on a null-valued expression.At
C:\Users\XXXX\Documents\WindowsPowerShell\Modules\foo\foo.psm1:37 char:5
+ $PSCmdlet.SessionState.PSVariable.Set('Value',$null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Is there a way to do this?

Get a line number where Write-Error occurred

Consider the script
$someComplexCondition = $false
if ($someComplexCondition)
{
Write-Error -Message "Some complex condition"
}
else
{
Write-Error -Message "Other complex condition"
}
When I run it it says
C:\Dev> .\MyScript.ps1
C:\Dev\MyScript.ps1 : Other complex condition
At line:1 char:15
+ .\MyScript.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,MyScript.ps1
And I noticed that Write-Error always reports itself as at line:1
According to the stack trace it looks like it's line #1 because it was called from the shell and more interesting char:15 because
".\MyScript.ps1 ".Length -eq 15
If we change file name, this char:15 will be changed accordingly.
The question is how to get the actual line when the error occurred.
In our case I would like to get line:9
When there is an actual error, generated by Powershell you get an object of the type ErrorRecord :
$error[0] | Get-Member
TypeName: System.Management.Automation.ErrorRecord
When you use this same ErrorRecord with Write-Error, you don't get an object :
$myError = Write-Error -ErrorRecord $Error[0]
PS C:\> $MyError | Get-Member
gm : You must specify an object for the Get-Member cmdlet.
At line:1 char:12
+ $MyError | gm
+ ~~
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
So now, we know that Write-Error will not give us anything that we can reuse later.
You can use the "Throw" statement, instead :
$someComplexCondition = $false
if ($someComplexCondition)
{
Throw "Some complex condition"
}
else
{
Throw "Other complex condition"
}
Then, when you run the script, the error gives you the line number and character number of the start of the "Throw" statement :
C:\Test-Error.ps1
Other complex condition
At C:\Test-Error.ps1:9 char:5
+ Throw "Other complex condition"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Other complex condition:String) [], RuntimeException
+ FullyQualifiedErrorId : Other complex condition
Here, it is line number 9.

I need help find the error in powershell script

Trying to get my script to work and need some help here is my code.
#excel
#open ap
$XL = new-object -com "Excel.Application"
$XLbooks = $XL.workbooks
$netci = [system.Globalization.CompareInfo]"en-us"
$wkbk = $XLbooks.PSBase.GetType().Invokemember("Add",[Reflection.BindingFlags]::InvokeMethod,$null,$XLbooks,$null,$newci)
$sheet = $XLbooks.worksheets.item(1)
$sheet.name = "name"
$sheet.cells.item($row,1).formulalocal = "Fred Nurk"
$file = "c\windows\scripts\test.xlsx"
[void]$wkbk.PSBase.GetType().InvokeMember("SaveAs",[Reflection.BindingFlags]::InvokeMethod,$null,$wkbk,$file,$newci)
("Close",[Reflection.BindingFlags]::Invokemedthod,$null,$wkbk,0,$newci)
$XL.quit()
Errors:
Cannot convert the "en-us" value of type "System.String" to type "System.Globalization.CompareInfo".
At C:\scripts\test.ps1:5 char:44
+ $netci = [system.Globalization.CompareInfo] <<<< "en-us"
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
You cannot call a method on a null-valued expression.
At C:\scripts\test.ps1:7 char:34
+ $sheet = $XLbooks.worksheets.item <<<< (1)
+ CategoryInfo : InvalidOperation: (item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Property 'name' cannot be found on this object; make sure it exists and is settable.
At C:\scripts\test.ps1:8 char:8
+ $sheet. <<<< name = "name"
+ CategoryInfo : InvalidOperation: (name:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
You cannot call a method on a null-valued expression.
At C:\scripts\test.ps1:9 char:18
+ $sheet.cells.item <<<< ($row,1).formulalocal = "Fred Nurk"
+ CategoryInfo : InvalidOperation: (item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Exception calling "InvokeMember" with "6" argument(s): "Microsoft Excel cannot access the file 'C:\Users\Jared\Document
s\c\windows\scripts\5ADD7000'. There are several possible reasons:
The file name or path does not exist.
The file is being used by another program.
The workbook you are trying to save has the same name as a currently open workbook."
At C:\scripts\test.ps1:11 char:42
+ [void]$wkbk.PSBase.GetType().InvokeMember <<<< ("SaveAs",[Reflection.BindingFlags]::InvokeMethod,$null,$wkbk,$file,$n
ewci)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodTargetInvocation
The main issue you need to address is creating the CompareInfo. The first error tells you this line isn't working:
$netci = [system.Globalization.CompareInfo]"en-us"
So what you'll need to do is create the CompareInfo object this way:
$netci = ([system.Globalization.CultureInfo]"en-us").CompareInfo
Though instead of using this crazy way to create a workbook:
$wkbk = $XLbooks.PSBase.GetType().Invokemember("Add",[Reflection.BindingFlags]::InvokeMethod,$null,$XLbooks,$null,$newci)
...try this more sane way instead :D
$wkbk = $XL.workbooks.Add()
If you do it this way, you won't have to worry about creating the CompareInfo object.
The problem that jumps out at me is that $netci = [system.Globalization.CompareInfo]"en-us" is invalid syntax. You're not calling any method on the System.Globalization.CompareInfo class, you're just placing a string after it. PowerShell is interpreting [System.Globalization.CompareInfo] as a typecast operator, and complains that the string "en-us" can't be converted to the data type System.Globalization.CompareInfo - because that data type doesn't exist.
You need to invoke a method that operates on "en-us". You can get a list of methods from MSDN:
http://msdn.microsoft.com/en-us/library/system.globalization.compareinfo.aspx
Assuming that the method you want is GetCompareInfo (seems most likely to me - it returns a CompareInfo object), you'd write that line this way:
$netci = [System.Globalization.CompareInfo]::GetCompareInfo('en-us')
Note, BTW, that you have this variable as $netci when you create it, but as $newci in the rest of the script.
I haven't looked too deeply at the other errors, but they're probably a cascade effect from the failure to create $newci properly, so I suspect that if you fix this, the other errors will go away.