tags:

views:

230

answers:

2

I have some PowerShell hosts in C# from where I execute PowerShell script code. The code below is from a host in an Add0In for Visual Studio. Problem is that if an error occurs in the PowerShell script code that I don't know the file and line number of the PowerShell script file where the error occured.

My hosting code looks like:

        public Exception Execute(string scriptcode, Hashtable variables)
    {
        Runspace runspace = null;
        Pipeline pipeline = null;
        PipelineStateInfo info = null;

        try
        {
            // Make our output window active
            Logger.ShowOutputWindow();

            // Create the runspace and stuff.
            runspace = RunspaceFactory.CreateRunspace(host);
            pipeline = runspace.CreatePipeline();

            runspace.Open();

            // Add our scriptcode to the pipeline
            pipeline.Commands.Add(new Command(scriptcode, true));

            // We don't want to get PSObjects out of the pipeline, output result as string in default way
            pipeline.Commands.Add(new Command("Out-Default", true));

            // Set up global variables
            FillVariables(runspace, variables);

            SetUpOutputStreams(pipeline);


            // Run the scriptcode
            Collection<PSObject> psOutput = pipeline.Invoke();

            // Did it complete ok?
            info = pipeline.PipelineStateInfo;
            if (info.State != PipelineState.Completed)
            {
                return info.Reason;
            }
            else
            {
                return null; // succesful!
            }
        }
        catch (Exception ex)
        {
            return ex;
        }
    }

First I had my script in the scriptcode variable, I now write the code to a temporary .ps1 file first so I could report on linenumbers in that file. But I can't find how to execute code in a file so it is possible to retrieve filename and line numbers in case of errors.

Any ideas?

A: 

This should get you in the right place:

//invoke pipeline
collection = pipeline.Invoke();

// check for errors (non-terminating)
if (pipeline.Error.Count > 0)
{
  //iterate over Error PipeLine until end
  while (!pipeline.Error.EndOfPipeline)
  {
    //read one PSObject off the pipeline
    var value = pipeline.Error.Read() as PSObject;
    if (value != null)
    {
      //get the ErrorRecord
      var r = value.BaseObject as ErrorRecord;
      if (r != null)
      {
        //build whatever kind of message your want
        builder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
        builder.AppendLine(r.InvocationInfo.PositionMessage);
        builder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
        builder.AppendLine(
        string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
      }
    }
  }
  return builder.ToString();
}

UPDATE:

As well as the information I wrote in the comment, please also look at this book: Professional PowerShell Programming

I found this book invaluable when I first started programming a host for the PowerShell runtime. It was written by some of the PowerShell devs.

James Pogran
Strange thing is that if an error occurs, for example using throw or 1/0, the pipeline.Invoke() throws an exception, and the error collection is empty. So I don't get the invocation information. It also does not make tracing work (set-psdebug -trace 1).
Serge van den Oever
Yes. This code will catch any errors PowerShell surfaces, not exceptions PowerShell throws. Those have to be caught in a normal try catch block around your invoke call. I usually have NullReferenceException and some specific PowerShell exceptions caught for specific cases.Tracing is another pipeline. I don't have the SDK in front of me at the moment, but I believe it's accessed the same way.
James Pogran
A: 

using a similar mechanism to the above code, I see that sometimes (pipeline.Error.Count > 0) and also (pipeline.Error.EndOfPipeline == false) but when I call pipeline.Error.Read() the result is null...

What gives?

verdeIguana