简体   繁体   中英

Pass parameter by reference to cmdlet in PowerShell

I am trying to write a simple PowerShell cmdlet in C# that accepts an instance of ArrayList and adds an item to it. The issue I've encountered is that a copy of the array list is passed to cmdlet, not the actual object as I would expect. Is there a way to pass parameter by reference? The following code outputs just "Count is 0" for ArrayList $B.

Note: to run the program in PowerShell 7.1 you need to target .NET 5 and install Microsoft.PowerShell.SDK and System.Management.Automation Nuget packages.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace CustomPowerShellCmdlet
{
    class Program
    {
        static void Main(string[] args)
        {
            string powerShellScript = @"

$B  = New-Object System.Collections.ArrayList
Add-CollectionItem -Collection $B -Item '1'
Add-CollectionItem -Collection $B -Item '2'

Write-Output  ('Count is ' + $B.Count)
            ";


            var output = RunPowerShellScript(powerShellScript);

            foreach (PSObject psObject in output)
            {
                Console.WriteLine(psObject.BaseObject.ToString());
            }
        }

        private static Collection<PSObject> RunPowerShellScript(string powerShellScript)
        {
            InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
            initialSessionState.Commands.Add(new SessionStateCmdletEntry("Add-CollectionItem", typeof(AddCollectionItemCmdlet), null));

            using (var runspace = RunspaceFactory.CreateRunspace(initialSessionState))
            {
                runspace.Open();

                using (var ps = PowerShell.Create())
                {
                    ps.Streams.Error.DataAdding += Output_DataAdding;
                    ps.Streams.Debug.DataAdding += Output_DataAdding;
                    ps.Runspace = runspace;

                    ps.AddScript(powerShellScript);

                    return ps.Invoke();
                }
            }
        }

        private static void Output_DataAdding(object sender, DataAddingEventArgs e)
        {
            Console.WriteLine(e.ItemAdded.ToString());
        }

        [Cmdlet(VerbsCommon.Add, "CollectionItem")]
        public class AddCollectionItemCmdlet : Cmdlet
        {
            [Parameter(Mandatory = true)]
            [AllowEmptyCollection]
            public ArrayList Collection { get; set; }

            [Parameter(Mandatory = true)]
            public object Item { get; set; }

            /// <summary>
            /// This static field is needed for demo purposes only.
            /// </summary>
            public static List<object> AddedItems = new List<object>();

            protected override void BeginProcessing()
            {
                Console.WriteLine("A new collection is passed to the cmdlet: {0}", !AddedItems.Any(x => ReferenceEquals(x, Collection)));
                AddedItems.Add(Collection);

                Collection.Add(Item);
            }
        }
    }
}

UPD1. It appears this behavior is caused by New-Object cmdlet. If I replace it with [System.Collections.ArrayList] @() then I get what I need. Still I don't understand why it works this way.

The object is being passed to the cmdlet, but it is not being returned, ie set is called, but get is not. You'll need to return the collection via the pipeline and reassign it to the original variable. Here is the working code:

using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace CustomPowerShellCmdlet
{
    class Program
    {
        static void Main(string[] args)
        {
            string powerShellScript = @"
Write-Output ('PowerShell Version is ' + $PSVersionTable.PSVersion)

$A  = New-Object System.Collections.ArrayList
$A.Add('3') | Out-Null
Write-Output  ('Count is ' + $A.Count)

$B  = New-Object System.Collections.ArrayList
$B = (Add-CollectionItem -Collection $B -Item '1')
$B = (Add-CollectionItem -Collection $B -Item '2')
foreach ($Item in $B) {
    Write-Output $Item
}
Write-Output  ('Count is ' + $B.Count)
";
            var output = RunPowerShellScript(powerShellScript);

            foreach (PSObject psObject in output)
            {
                Console.WriteLine(psObject.BaseObject.ToString());
            }
        }

        private static Collection<PSObject> RunPowerShellScript(string powerShellScript)
        {
            InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
            initialSessionState.Commands.Add(new SessionStateCmdletEntry("Add-CollectionItem", typeof(AddCollectionItemCmdlet), null));

            using (var runspace = RunspaceFactory.CreateRunspace(initialSessionState))
            {
                runspace.Open();
                using (var ps = PowerShell.Create())
                {
                    ps.Runspace = runspace;
                    ps.AddScript(powerShellScript);
                    return ps.Invoke();
                }
            }
        }

        [Cmdlet(VerbsCommon.Add, "CollectionItem")]
        public class AddCollectionItemCmdlet : Cmdlet
        {
            [Parameter(Mandatory = true)]
            [AllowEmptyCollection]
            public ArrayList Collection
            {
                get; set;
            }

            [Parameter(Mandatory = true)]
            public object Item { get; set; }

            protected override void ProcessRecord()
            {
                Collection.Add(Item);
                WriteObject(Collection);
            }
        }
    }
}

It appeared this is a bug in PowerShell, see https://github.com/PowerShell/PowerShell/issues/14394

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM