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

LVL 15
David L. HansenCEOAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Chris DentPowerShell DeveloperCommented:
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.
0
Chris DentPowerShell DeveloperCommented:
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.
0
David L. HansenCEOAuthor Commented:
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)?
0
Simplify Active Directory Administration

Administration of Active Directory does not have to be hard.  Too often what should be a simple task is made more difficult than it needs to be.The solution?  Hyena from SystemTools Software.  With ease-of-use as well as powerful importing and bulk updating capabilities.

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

Open in new window

0
Chris DentPowerShell DeveloperCommented:
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

0
David L. HansenCEOAuthor Commented:
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.
0
David L. HansenCEOAuthor Commented:
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?
0
Chris DentPowerShell DeveloperCommented:
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.
0
David L. HansenCEOAuthor Commented:
Working on that now....
0
Fernando SotoRetiredCommented:
Have a look at this web page. Calling a PowerShell Script From Your .NET Code.
0
David L. HansenCEOAuthor Commented:
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!
0
Chris DentPowerShell DeveloperCommented:
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.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
David L. HansenCEOAuthor Commented:
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
0
Chris DentPowerShell DeveloperCommented:
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
0
David L. HansenCEOAuthor Commented:
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?
0
Chris DentPowerShell DeveloperCommented:
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).
0
David L. HansenCEOAuthor Commented:
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!
0
Chris DentPowerShell DeveloperCommented:
Yay :) Good work :)
0
David L. HansenCEOAuthor Commented:
A big thank you to everyone who helped!
Hat-tip to David J. and Fernando.
0
David L. HansenCEOAuthor Commented:
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.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Powershell

From novice to tech pro — start learning today.