Powershell cannot pass object to function - powershell

I am unable to pass an object of type [System.Messaging.Message] into a function within my script.
e.g. (outline of code)
function global:CopyQueue() {
$vTotalCountInMSMQ = $global:qSource.GetAllMessages()
foreach ($msg in $vTotalCountInMSMQ)
{
ReadAndCopyMessage $destinationQueue ([REF]$msg)
}
}
Target Function:
function global:ReadAndCopyMessage($destinationQueueName, [REF]$message)
{
$message = $message.BodyStream.Position
.etc.....
}
Unable to access properties (Property 'Position' cannot be found on this object; make sure it exists and is settable.). However, if this code is run within the CopyQueue function, everything works as expected.
I am having trouble to outsource this and process the $msg object out of the loop.
Thanks for your help in advance
similiar questions didn't work:
PowerShell pass by reference not working for me
Powershell argument passing to function seemingly not working

It appears that you shouldn't use [REF] anymore. Also, I must have made the common "," error between parameters before and thus it didn't work.
The code above works fine without [REF]
Call:
ReadAndCopyMessage $destinationQueue $msg
Function:
function global:ReadAndCopyMessage($destinationQueueName, $message)

Related

PowerShell scriptblock scope weirdness

I am using Invoke-Command on the local (not remote) computer to run a scriptblock that includes a function. Some of the variables defined at the "root" of the scriptblock are available in the function, and some require me to scope them as Global:.
So in quasi-code, this fails:
{
function test() {
if (test-path $filepath.trim('t')) {
$taskFolder.gettasks(1) |out-file '$env:temp\tasks.txt'
}
}
$filepath = '$env:temp\test.txt'
$TaskService = new-object -ComObject('Schedule.Service')
$TaskService.connect()
$TaskFolder = $TaskService.GetFolder('\')
test
}
with a "You cannot call a method on a null-valued expression." (i.e. $filepath -eq $null).
However, this works:
{
function test() {
if (test-path $Global:filepath.trim('t')) {
$taskFolder.gettasks(1) |out-file '$env:temp\tasks.txt'
}
}
$Global:filepath = '$env:temp\test.txt'
$TaskService = new-object -ComObject('Schedule.Service')
$TaskService.connect()
$TaskFolder = $TaskService.GetFolder('\')
test
}
even though $TaskFolder is not Global.
I can't seem to replicate the problem with short, simple code like this here, but the effect of ONLY globally scoping a couple variables makes all the difference.
I initially noticed the issue when I was attempting to use the variables with Script: scope and that was failing. I assumed that was because this was a script block and not an actual script. Changing to Global: resolved the problem and then I noticed the non-scoped $taskfolder variable.
I am not asking about scheduled tasks! I only included it in the example because that is the variable that is working fine without being assigned a global scope.
Obviously, I'm not understanding something about scoping, invoking, or maybe the Schedule Service.
Can someone enlighten me?

How can I call a method on multiple objects in PowerShell?

I am working with an XAML GUI created in VisualStudio, and I am trying to set up some of the code for the checkboxes. Instead of doing each one separately, I was hoping to do it dynamically since the functions are all the same.
I have searched, but was only able to find a reference to an Invoke-Method function that was written.
Below is what I have so far, just to explain better what I am trying to accomplish.
The Get-Variable line comes back with a half-dozen variables that I am trying to call the same method on.
$vars = Get-Variable WPFVarA*
Foreach ( $var in $vars ) {
$var.Add_Checked.Invoke({$Global:TicketCount++})
$var.Add_Unchecked.Invoke({$Global:TicketCount--})
}
If the checkboxes are wrapped, say with a stackpanel (could be anything) you can create a routed even handler to handle all events, like this (untested):
#Bubble up event handler
[Windows.RoutedEventHandler]$Script:CheckedEventHandler = {
$Global:TicketCount++
}
[Windows.RoutedEventHandler]$Script:UncheckedEventHandler = {
$Global:TicketCount--
}
$StackPanel.AddHandler([Windows.Controls.CheckBox]::CheckedEvent, $CheckedEventHandler)
$StackPanel.AddHandler([Windows.Controls.CheckBox]::UncheckedEvent, $UncheckedEventHandler)

PowerShell AST FindAll method take a scriptblock with $args[0]

I have been working with the PowerShell AST to create some custom rules for PSScriptAnalyzer.
In a lot of the example code for AST, there is one line that I don't understand. Here is an example.
First parse a file, in this case, the current open file in the ISE.
$AbstractSyntaxTree = [System.Management.Automation.Language.Parser]::
ParseInput($psISE.CurrentFile.Editor.Text, [ref]$null, [ref]$null)
This makes sense so far. Let's say that we want to look for all the ParameterAst objects. The code that I have seen to do this is below.
$params = $AbstractSyntaxTree.FindAll({$args[0] -is [System.Management.Automation.Language.ParameterAst]}, $true)
This line of code is calling FindAll and passing in a scriptblock, that seems to be acting as a filter, so that only ParameterAst objects are returned.
What I don't understand here is how $args[0] fits into this call. How are any parameters actually getting passed into the scriptblock when the FindAll method is invoked?
FindAll method has following signature (from msdn):
public IEnumerable<Ast> FindAll (
Func<Ast,bool> predicate,
bool searchNestedScriptBlocks
)
So first argument is a delegate that takes Ast as input, and returns bool.
In Powershell you can create such delegate like that:
$delegate = { param($ast) $ast -is [System.Management.Automation.Language.ParameterAst] }
Or without declaring parameter:
$delegate = { $args[0] -is [System.Management.Automation.Language.ParameterAst] }
FindAll method will then do something like that (pseudocode):
foreach ($node in $allNodes) {
$shouldAdd = & $delegate $node <-- this is how $node gets passed to your delegate
if ($shouldAdd) {
<add the node to the output list>
}
}
Think of the scriptblock as an anonymous callback function.
It's really the same thing that happens when you use Where-Object { $someCondition }.
.FindAll finds all the (things) and for each one it calls the function you provided it. It's apparently expecting a [bool] result, and returning the objects that satisfied the conditions present in the callback.
In a function or script or scriptblock in powershell, you can have named parameters that are explicitly defined, or you can reference parameters without declaring them using the $args array, which is what's happening here.
Using a scriptblock as a callback is similar to using it for an event:
$Args
Contains an array of the undeclared parameters and/or parameter
values that are passed to a function, script, or script block.
When you create a function, you can declare the parameters by using the
param keyword or by adding a comma-separated list of parameters in
parentheses after the function name.
In an event action, the $Args variable contains objects that represent
the event arguments of the event that is being processed.

How does name lookup work in Powershell script blocks?

User cashfoley has posted what appears to be a fairly elegant set of code at codeplex for a "module" called PSClass.
When I dot-source the psclass code into some code of my own, I am able to write code like:
$Animal = New-PSClass Animal {
constructor {
param( $name, $legs )
# ...
}
method -override ToString {
"A $($this.Class.ClassName) named $($this.name) with $($this.Legs) Legs"
}
}
When I tried to create a module out of the PSClass code, however, I started getting errors. The constructor and method names are no longer recognized.
Looking at the actual implementation, what I see is that constructor, method, etc. are actually nested functions inside the New-PSClass function.
Thus, it seems to me that when I dot-source the PSClass.ps1 file, my script-blocks are allowed to contain references to functions nested inside other local functions. But when the PSClass code becomes a module, with the New-PSClass function exported (I tried both using a manifest and using Export-ModuleMember), the names are no longer visible.
Can someone explain to me how the script blocks, scoping rules, and visibility rules for nested functions work together?
Also, kind of separately, is there a better class definition protocol for pure Powershell scripting? (Specifically, one that does not involve "just write it in C# and then do this...")
The variables in your script blocks don't get evaluated until they are executed. If the variables in the script block don't exist in the current scope when the block is executed, the variables won't have any values. Script blocks aren't closures: they don't capture the context at instantiation time.
Remove-variable FooBar
function New-ScriptBlock
{
$FooBar = 1
$scriptBlock = {
Write-Host "FooBar: $FooBar"
}
$FooBar = 2
& $scriptBlock # Outputs FooBar: 2 because $FooBar was set to 2 before invocation
return $scriptBlock
}
function Invoke-ScriptBlock
{
param(
$ScriptBlock
)
& $ScriptBlock
}
$scriptBlock = New-ScriptBlock
& $scriptBlock # Prints nothing since $FooBar doesn't exist in this scope
$FooBar = 3
Invoke-ScriptBlock $scriptBlock # Prints $FooBar: 3 since FooBar set to 3

PowerShell function won't return object

I have a simple function that creates a generic List:
function test()
{
$genericType = [Type] "System.Collections.Generic.List``1"
[type[]] $typedParameters = ,"System.String"
$closedType = $genericType.MakeGenericType($typedParameters)
[Activator]::CreateInstance($closedType)
}
$a = test
The problem is that $a is always null no matter what I try. If I execute the same code outside of the function it works properly.
Thoughts?
IMHO that's pitfall #1. If you return an object from the function that is somehow enumerable (I don't know exactly if implementing IEnumerable is the only case), PowerShell unrolls the object and returns the items in that.
Your newly created list was empty, so nothing was returned. To make it work just use this:
,[Activator]::CreateInstance($closedType)
That will make an one item array that gets unrolled and the item (the generic list) is assigned to $a.
Further info
Here is list of similar question that will help you to understand what's going on:
Powershell pitfalls
Avoiding Agnostic Jagged Array Flattening in Powershell
Strange behavior in PowerShell function returning DataSet/DataTable
What determines whether the Powershell pipeline will unroll a collection?
Note: you dont need to declare the function header with parenthesis. If you need to add parameters, the function will look like this:
function test {
param($myParameter, $myParameter2)
}
or
function {
param(
[Parameter(Mandatory=true, Position=0)]$myParameter,
... again $myParameter2)
...
An easier way to work with generics. This does not directly solve the [Activator] approach though
Function test
{
New-Object "system.collections.generic.list[string]"
}
(test).gettype()