How to get a Powershell script to launch from Visual Studio

David L. Hansen
David L. Hansen used Ask the Experts™
on
I have a PS script (myscript.ps1) that runs fine when launched from the PS command line. However, when I launch it from a Visual Studio app using the following code, the PS blue screen flashes quickly but nothing else happens (the expectation is the creation of a file). There doesn't appear to be any errors but I suspect one is listed in PS's blue screen.

Btw, launching PS scripts from VS is new to me, although I have set PS's rights and it's trusted folders already.

Process.Start("powershell", "-noexit -file 'c:\Temp\myScript.ps1'")

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Chris DentPowerShell Developer
Top Expert 2010

Commented:
Depending on your goals there's another way to go about this.

A mini-class with a PS wrapper :)
Add-Type -TypeDefinition @"
using System.Management.Automation;
using System.Management.Automation.Runspaces;

public class PS
{
    PowerShell host = PowerShell.Create(InitialSessionState.CreateDefault());

    public object RunScript(string path)
    {
        return host.AddScript(path).Invoke();
    }
}
"@ -ReferencedAssemblies ([PowerShell].Assembly.Location)

Open in new window

Once it's created, the method can be invoked after you instantiate the class.
[PS]::new().RunScript('C:\Development\scratch\test.ps1')

Open in new window

The advantage of this approach is that you can get detailed error information. Runtime errors (terminating errors) will throw as exceptions when Invoke is called. Non-terminating errors are written to host.Streams.Error.
Chris DentPowerShell Developer
Top Expert 2010

Commented:
I just noticed the VB .NET tag. Is the C# example easy enough for you to translate? I can change it, but I always have to double check VB .NET syntax which is only worthwhile if you find it to be.
No Chris, C# is fine. I'll give it a shot and see how it goes.

ps. I have a simple text log file created which I'd like to be filled with any errors. Can I pass the errors listed in the host.Streams.Error object to that log file easily (If it's not too much trouble...I can open another question for that if you need)?
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

Top Expert 2016

Commented:
Process.Start("powershell", "-noexit  -ExecutionPolicy Bypass -file 'c:\Temp\myScript.ps1'")

Open in new window

Chris DentPowerShell Developer
Top Expert 2010

Commented:
Yes, you can. The Error collection contains a list of ErrorRecord objects. This example should push that to the console, it shows how I would distinguish between the different types of error (runtime vs managed).
Add-Type -TypeDefinition @"
Imports System
Imports System.Management.Automation
Imports System.Management.Automation.Runspaces

Public Class PS
    Dim host As PowerShell = PowerShell.Create(InitialSessionState.CreateDefault())

    Public Function RunScript(path As String) As object
        Try
            Dim value As Object = host.AddScript(path).Invoke()
            If host.HadErrors Then
                For Each errorRecord as ErrorRecord in host.Streams.Error
                    Console.WriteLine(errorRecord.Exception.Message)
                Next
            Else
                Return value
            End If
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try

        Return Nothing
    End Function
End Class
"@ -ReferencedAssemblies ([PowerShell].Assembly.Location) -Language VisualBasic

Open in new window

David,

Your "Process.Start" approach results in the same blue screen flash as before and no file creation. I'll keep watching for more suggestions from you while I test Chris's code.
Chris,

The "Host" object that we're instantiating has no "Create" method attached to it (at least not in the namespace I'm seeing). It does however, have a CreateNestedPowerShell method instead. If I use that one instead, I do find the "AddScript" method but  no "HadErrors" property.

I'm using/importing all three namespaces you have in your example. I'm also running VS2015.
Thoughts?
Chris DentPowerShell Developer
Top Expert 2010

Commented:
It's a static method on the System.Management.Automation.PowerShell class. Be a touch careful about assembly versions though, Visual Studio is terrible at picking a modern version when selecting references. I normally feed it the path revealed by the snippet below when adding the reference:
[PowerShell].Assembly.Location

Open in new window

That gets it the assembly associated with PowerShell 5.1 not PowerShell 2.0.
Working on that now....
Fernando SotoRetired
Distinguished Expert 2017

Commented:
Have a look at this web page. Calling a PowerShell Script From Your .NET Code.
Chris,

Like you say, I think if we can persuade VS to take a different PowerShell version, we'd be home free.
This code however:
"Add-Type -TypeDefinition @" and "@ -ReferencedAssemblies ([PowerShell].Assembly.Location) -Language VisualBasic"

Open in new window

surrounding the class code causes the entire class code to display as quoted-text and red-marks the whole thing with a "Statement cannot appear outside of a method body" error.

I'm putting this in an existing VB project so the VB format would be preferable, but whether in C# or VB, can you show me a working example of the ReferencedAssemblies approach please? I don't mind rewriting it to fit my environment/local-paths, etc.

Thanks in advance!
PowerShell Developer
Top Expert 2010
Commented:
Apologies, for your VB project drop all of the Add-Type pieces, they were only so I could past the class into the PS console.

So this goes in as source code:
Imports System
Imports System.Management.Automation
Imports System.Management.Automation.Runspaces

Public Class PS
    Dim host As PowerShell = PowerShell.Create(InitialSessionState.CreateDefault())

    Public Function RunScript(path As String) As object
        Try
            Dim value As Object = host.AddScript(path).Invoke()
            If host.HadErrors Then
                For Each errorRecord as ErrorRecord in host.Streams.Error
                    Console.WriteLine(errorRecord.Exception.Message)
                Next
            Else
                Return value
            End If
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try

        Return Nothing
    End Function
End Class

Open in new window

And the referenced assemblies goes in...

1. Open PS and run: [PowerShell].Assembly.Location and copy the path ([PowerShell].Assembly.Location | clip)
2. In Visual Studio
3. Select the project
4. References
5. Add Reference...
6. Browse (button, bottom right)
7. Paste the path from 1 and press Add

At this point you should have a much more up to date assembly loaded, and hopefully it'll support some of the methods we've been using.
This is the resulting path given by PS:

"C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management
.Automation.dll"

Yesterday actually, I included that particular reference in the project, which was when I got the strange "CreateNestedPowerShell" method instead of the simpler "Create". Luckily, it does appear that I can download any version of the various PS namespaces through NuGet though, but I'm not sure which I need.

Here are the options:
https://www.nuget.org/profiles/PowerShellTeam
Chris DentPowerShell Developer
Top Expert 2010

Commented:
Hmm okay, well at least that bit is right.

The CreateNestedPowerShell method is a method on the instantiated host, we don't need that one. To create the host in the first place you want the static method on the PowerShell type.

Where this:
PowerShell.Create(InitialSessionState.CreateDefault())

Open in new window

Is this static method:

https://msdn.microsoft.com/en-us/library/hh470726(v=vs.85).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Using the url you just posted, I tracked backwards (through the webpage's bread-crumb path) and found no specific version number referenced. Perhaps you can look in your object explorer in VS and see the correct version number that way?
Chris DentPowerShell Developer
Top Expert 2010

Commented:
This is my assemblies full name:

System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

I think you have the right assembly.

Going back to this:

> The "Host" object that we're instantiating has no "Create" method attached to it

The create method is not on the instantiated "Host" object. It's a static method on the PowerShell type (does not require an instantiated object).

You're right that the instantiated "Host" object does not have such a method. But if you've instantiated it, you're done with that part and it's on to loading a script into the Commands collection (using the AddScript method).
It worked! :)

Yesterday, for whatever reason (operator error I'm sure) this line of code:
Dim host As PowerShell = PowerShell.Create(InitialSessionState.CreateDefault())

Open in new window

wasn't liked by VS so I changed it to this:
Dim host As PowerShell

Open in new window

and followed that up with the "Create" static method used in a constructor (Sub New) which it didn't like either (saying that a "declaration is expected").

At any rate, it works!

Thank you, thank you!
Chris DentPowerShell Developer
Top Expert 2010

Commented:
Yay :) Good work :)
A big thank you to everyone who helped!
Hat-tip to David J. and Fernando.
For any future readers of this thread, I later on needed to get this working across a network which required my changing some things. The scope and ExecutionPolicy needed to be altered and since catching all errors was still important to me (which can't be done through a simple shell process) I posted this additional question which was very useful.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial