views:

212

answers:

1

Hello, I'm calling a powershell script from C#. The script is pretty small and is "gps;$host.SetShouldExit(9)", which list process, and then send back an exit code to be captured by the PSHost object.

The problem I have is when the pipeline has been stopped and disposed, the output reader PSHost collection still seems to be written to, and is filling up. So when I try and copy it to my own output object, it craps out with a OutOfMemoryException when I try to iterate over it. Sometimes it will except with a Collection was modified message. Here is the code.

 private void ProcessAndExecuteBlock(ScriptBlock Block)
   {
       Collection<PSObject> PSCollection = new Collection<PSObject>();
       Collection<Object> PSErrorCollection = new Collection<Object>();
       Boolean Error = false;
       int ExitCode=0;

       //Send for exection. 
       ExecuteScript(Block.Script);

       // Process the waithandles. 
       while (PExecutor.PLine.PipelineStateInfo.State  == PipelineState.Running)
       {
           // Wait for either error or data waithandle. 
           switch (WaitHandle.WaitAny(PExecutor.Hand))
           {
               // Data
               case 0:
                   Collection<PSObject> data =   PExecutor.PLine.Output.NonBlockingRead();
                   if (data.Count  > 0)
                   {
                       for (int cnt = 0; cnt <= (data.Count-1); cnt++)
                       {
                           PSCollection.Add(data[cnt]); 
                       }
                   }

                   // Check to see if the pipeline has been closed. 
                   if (PExecutor.PLine.Output.EndOfPipeline)
                   { 
                       // Bring back the exit code. 
                       ExitCode = RHost.ExitCode; 
                   }
                   break;
               case 1:
                   Collection<object> Errordata = PExecutor.PLine.Error.NonBlockingRead();
                   if (Errordata.Count > 0)
                   {
                       Error = true;
                       for (int count = 0; count <= (Errordata.Count - 1); count++)
                       {
                           PSErrorCollection.Add(Errordata[count]);
                       }
                   }
                   break;
           }
       }

       PExecutor.Stop();

       // Create the Execution Return block
       ExecutionResults ER = new ExecutionResults(Block.RuleGuid,Block.SubRuleGuid, Block.MessageIdentfier);
       ER.ExitCode = ExitCode;

       // Add in the data results.
       lock (ReadSync)
       {
           if (PSCollection.Count > 0)
           {
               ER.DataAdd(PSCollection);
           }
       }

       // Add in the error data if any.
       if (Error)
       {
           if (PSErrorCollection.Count > 0)
           {
               ER.ErrorAdd(PSErrorCollection);
           }
           else
           {
               ER.InError = true;
           }
       }

       // We have finished, so enque the block back. 
       EnQueueOutput(ER);
   }

and this is the PipelineExecutor class which setups the pipeline for execution.

public class PipelineExecutor
{
    private Pipeline pipeline;
    private WaitHandle[] Handles;

    public Pipeline PLine
    {
        get { return pipeline; }
    }

    public WaitHandle[] Hand 
    {
        get { return Handles; }
    }

    public PipelineExecutor(Runspace runSpace, string command)
    {       
        pipeline = runSpace.CreatePipeline(command);
        Handles = new WaitHandle[2];
        Handles[0] = pipeline.Output.WaitHandle;
        Handles[1] = pipeline.Error.WaitHandle;
    }

    public void Start()
    {
        if (pipeline.PipelineStateInfo.State == PipelineState.NotStarted)
        {
            pipeline.Input.Close();
            pipeline.InvokeAsync();
        }
    }

    public void Stop()
    {
        pipeline.StopAsync();
    }
}

An this is the DataAdd method, where the exception arises.

    public void DataAdd(Collection<PSObject> Data)
    {
        foreach (PSObject Ps in Data)
        {
            Data.Add(Ps);
        }
    }

I put a for loop around the Data.Add, and the Collection filled up with 600k+ so feels like the gps command is still running, but why. Any ideas.

Thanks in advance.

A: 

Found the problem. Named the resultant collection and the iterator the same, so as it was iterating, it was adding to the collection, and back into the iterator, and so forth. Doh!.

scope_creep