Link to home
Start Free TrialLog in
Avatar of David L. Hansen
David L. HansenFlag for United States of America

asked on

How to get a Powershell script to launch from Visual Studio

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

Avatar of Chris Dent
Chris Dent
Flag of United Kingdom of Great Britain and Northern Ireland image

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.

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.
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.
Avatar of David L. Hansen


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)?
Process.Start("powershell", "-noexit  -ExecutionPolicy Bypass -file 'c:\Temp\myScript.ps1'")

Open in new window

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
            Dim value As Object = host.AddScript(path).Invoke()
            If host.HadErrors Then
                For Each errorRecord as ErrorRecord in host.Streams.Error
                Return value
            End If
        Catch e As Exception
        End Try

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

Open in new window


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.

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.
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:

Open in new window

That gets it the assembly associated with PowerShell 5.1 not PowerShell 2.0.
Working on that now....

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!
Avatar of Chris Dent
Chris Dent
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
This is the resulting path given by PS:


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:
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:

Open in new window

Is this static method:
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?
This is my assemblies full name:

System.Management.Automation, Version=, 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!
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.