launching a powershell script from visual studio 2010

I have developed a service that simply watches for a file to be created, then executes a powershell script in an Exchange 2010 environment.
I have tried using a runspace, but find that in order to add the needed snap-in, I must use VS2012 (which I don't have in my lab) so that it will run the 64-bit version of powershell.  I've also found a few other hiccups with this approach, seeming that it's difficult to troubleshoot where it breaks down in the script (which works fine outside of the visual studio project).  I've also read that Exchange 2010 has to launch a remote session, because local snap-ins are no longer supported? (Found at http://stackoverflow.com/questions/7811754/powershell-snapin-issues-in-c-sharp)

I'm now attempting just to launch a process in visual studio - using powershell.exe as the filename and supplying the script name and its parameters, but apparently that's causing issues too.  It seems that at least one of my parameters, enclosed in single quotes, is causing a problem.

Is there anyone familiar enough with running powershell scripts through visual studio that can help me get this working?
LVL 67
sirbountyAsked:
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.

David Johnson, CD, MVPOwnerCommented:
I must use VS2012 (which I don't have in my lab) so that it will run the 64-bit version of powershell.

Why not get Visual Studio Community Edition (free)
sirbountyAuthor Commented:
It's not so much getting my hands on a copy, it's just hard to get software into the lab...
Bob LearnedCommented:
You might try this approach:

Executing PowerShell scripts from C#
http://blogs.msdn.com/b/kebab/archive/2014/04/28/executing-powershell-scripts-from-c.aspx

Public Sub ExecuteAsynchronously()
	Using PowerShellInstance As PowerShell = PowerShell.Create()
		' this script has a sleep in it to simulate a long running script
		PowerShellInstance.AddScript("$s1 = 'test1'; $s2 = 'test2'; $s1; write-error 'some error';start-sleep -s 7; $s2")

		' prepare a new collection to store output stream objects
		Dim outputCollection As New PSDataCollection(Of PSObject)()
		outputCollection.DataAdded += outputCollection_DataAdded

		' the streams (Error, Debug, Progress, etc) are available on the PowerShell instance.
		' we can review them during or after execution.
		' we can also be notified when a new item is written to the stream (like this):
		PowerShellInstance.Streams.[Error].DataAdded += Error_DataAdded

		' begin invoke execution on the pipeline
		' use this overload to specify an output stream buffer
		Dim result As IAsyncResult = PowerShellInstance.BeginInvoke(Of PSObject, PSObject)(Nothing, outputCollection)

		' do something else until execution has completed.
		' this could be sleep/wait, or perhaps some other work
		While result.IsCompleted = False
			Console.WriteLine("Waiting for pipeline to finish...")

				' might want to place a timeout here...
			Thread.Sleep(1000)
		End While

		Console.WriteLine("Execution has stopped. The pipeline state: " + PowerShellInstance.InvocationStateInfo.State)

		For Each outputItem As PSObject In outputCollection
			'TODO: handle/process the output items if required
			Console.WriteLine(outputItem.BaseObject.ToString())
		Next
	End Using
End Sub

Open in new window

Creating Active Directory Users from a Text File

If your organization has a need to mass-create AD user accounts, watch this video to see how its done without the need for scripting or other unnecessary complexities.

sirbountyAuthor Commented:
Thanks for that Bob.
I hadn't seen that article, so I read through it and tried to devise something similar, but from other things I'd read I needed to add the snapin first - so I tried that as well and right away it comes back stating that 'no snap-ins' have been registered for Powershell version 3' - I'm not sure why or how to handle that error...any ideas?  I've tried it within the script as well and it still fails.
sirbountyAuthor Commented:
By 'add that first', I meant via:
PowerShellInstance.AddScript("Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010")
PowerShellInstance.AddScript(ScriptName)
PowerShellInstance.AddParameter("....", "some value")

Open in new window

sirbountyAuthor Commented:
Ok, I tried the version you posted above and I got many lines of
"waiting for pipeline to finish..."
but then it threw an invalid cast exception (from string "Execution has stopped. The pipl" to type 'Double' is not valid).

I did have to comment out
 'outputCollection.DataAdded += outputCollection_DataAdded
and
'PowerShellInstance.Streams..DataAdded += Error_DataAdded
as they were causing errors.
sirbountyAuthor Commented:
Found the exception in an output that I'd set in my script...file shows:
The term 'Get-DistributionGroup' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

So, it appears it's still not adding the snapin. :(
sirbountyAuthor Commented:
And sorry for the spam...
It seems to have 'worked' as the output file did state that an internal error was thrown that I'm familiar with - that a list by the same name exists, but even removing that from AD, it doesn't seem to complete without throwing the unhandled exception outlined above (and so far, hasn't recreated the previous duplicate).
I'm confused and not sure how I can troubleshoot from here.
Bob LearnedCommented:
1) That was a quick conversion from C# to VB.NET, without any regards to correctness.

PowerShellInstance.Streams.[Error].DataAdded += Error_DataAdded

Open in new window


should be

AddHandler PowerShellInstance.Stream.Error.DataAdded, Error_DataAdded

Open in new window


2) What line did you get this error on "but then it threw an invalid cast exception (from string "Execution has stopped. The pipl" to type 'Double' is not valid)"

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
sirbountyAuthor Commented:
Doesn't give me a line number - sometimes it happens after many 'waiting for pipeline to finish..." sometimes only a few. :\
I'll add the above...
sirbountyAuthor Commented:
Error_DataAdded is not declared.
sirbountyAuthor Commented:
Tried a conversion too...but couldn't get past this part either:
            Dim outputCollection As New PSDataCollection(Of PSObject)()
            outputCollection.DataAdded += outputCollection_DataAdded
outputCollection_DataAdded is not declared.
sirbountyAuthor Commented:
Even tried the comment at the bottom:
void Error_DataAdded(object sender, DataAddedEventArgs e) {
 var records = (PSDataCollection<ErrorRecord>)sender;
 // do something when an error is written to the error stream
 Console.WriteLine(records[e.Index].ToString());
}
sirbountyAuthor Commented:
After doing that, I can get this working:
AddHandler PowerShellInstance.Streams.Error.DataAdded, AddressOf Error_DataAdded

But the outputCollection.DataAdded += outputCollection_DateAdded
still causes problems.
sirbountyAuthor Commented:
That looks 'a little' better though... I get this the errors on the screen, but then it still throws the exception at the end (invalidcastexception)

Waiting for pipeline to finish...
Waiting for pipeline to finish...
No snap-ins have been registered for Windows PowerShell version 3.
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
The term 'Get-DistributionGroup' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
Waiting for pipeline to finish...
sirbountyAuthor Commented:
I suspect this goes back to the initial link about the snap-ins not being supported. :(
sirbountyAuthor Commented:
I tried using import-module to the Exchange management.dll.
It still errors, could not load file or assembly <file> or one of its dependencies.
Then it complains aobut get-distribution not being recognized,
then gives the invalidcast exception, (wish I knew where that was coming from).
:\
I can get a copy of vs2012 in my lab now, if that might help?
sirbountyAuthor Commented:
Maybe getting closer... I used this mechanism for adding a session:
https://technet.microsoft.com/en-us/library/dd297932.aspx

I say maybe because it doesn't seem to error on the cmdlets, but it's still bbombing on the cast exception (where is that? ugh)
Bob LearnedCommented:
My friend, you remind me of a Stanley engine--with just a little input, you run perpetually.

Can you show me what you currently have, please?
sirbountyAuthor Commented:
Heh heh - I dunno...I'm just trying to get this working.  Haha!
And for the most part, it is now (finally!).
However, it's still throwing that exception, and I have no idea how to track it down, nor identify any portion of the code to show you.
I've placed a try/catch in both the vs project and the powershell script, but I don't seem to be catching it, wherever it is..
The project calls one script, which calls another, which reads an xml file, and feeds all that back to the parent script.
If you have an idea where to start, let me know, otherwise, I'll continue running perpetually until I find this darned needle. ;^)
sirbountyAuthor Commented:
Thought it might have to do with this (http://www.experts-exchange.com/Programming/Languages/Scripting/Powershell/Q_28673119.html), but I've sorted that and still not getting past this exception.  That's more of a why did it work when I did this question, than 'give me an answer' question...
sirbountyAuthor Commented:
This is what I have for the piece that runs it in VS.
Any concern that the outputCollection.DataAdded piece is still commented?

 Using PowerShellInstance As PowerShell = PowerShell.Create()
            PowerShellInstance.AddScript(CommandString)
            Dim outputCollection As New PSDataCollection(Of PSObject)()
            'outputCollection.DataAdded += outputCollection_DataAdded

            ' the streams (Error, Debug, Progress, etc) are available on the PowerShell instance.
            ' we can review them during or after execution.
            ' we can also be notified when a new item is written to the stream (like this):
            'PowerShellInstance.Streams.[Error].DataAdded += Error_DataAdded
            AddHandler PowerShellInstance.Streams.Error.DataAdded, AddressOf Error_DataAdded
            Try
                Dim result As IAsyncResult = PowerShellInstance.BeginInvoke(Of PSObject, PSObject)(Nothing, outputCollection)
                Console.Write("Waiting for pipeline to finish")
                While result.IsCompleted = False
                    Console.Write(".")
                    Threading.Thread.Sleep(1000)
                End While
                Console.WriteLine(".")
                Console.WriteLine("Execution has stopped. The pipeline state: " + PowerShellInstance.InvocationStateInfo.State)

                For Each outputItem As PSObject In outputCollection
                    Console.WriteLine(outputItem.BaseObject.GetType().FullName)
                    Console.WriteLine(outputItem.BaseObject.ToString())
                Next
            Catch ex As Exception
                Console.WriteLine("Exception is " & ex.Message)
            End Try

        End Using

Open in new window

  Private Sub Error_DataAdded(sender As Object, e As DataAddedEventArgs)
        Dim records = DirectCast(sender, PSDataCollection(Of ErrorRecord))
        ' do something when an error is written to the error stream
        Console.WriteLine(records(e.Index).ToString())
    End Sub

Open in new window

Bob LearnedCommented:
Did you take the code that I showed you, and change it beyond the capability for sharing?
sirbountyAuthor Commented:
Oh no, that piece I think is working (other than perhaps my last comment).
That's what I have in the vs project.  I suppose now though that this is something that's being thrown from my powershell script, so I'll close this out and try to track that down.

Thanks (as always!)
Bob LearnedCommented:
I ran this sample code, which creates a script that generates an object, and then synchronously executes the script, to get back a collection of PSObject instances.

using System;
using System.Management.Automation;

namespace PowerShellExecutionSample
{
    /// <summary>
    /// Test class object to instantiate from inside PowerShell script.
    /// </summary>
    public class TestObject
    {
        /// <summary>
        /// Gets or sets the Name property
        /// </summary>
        public string Name { get; set; }
    }

    /// <summary>
    /// Test static class to invoke from inside PowerShell script.
    /// </summary>
    public static class TestStaticClass
    {
        /// <summary>
        /// Sample static method to call from inside PowerShell script.
        /// </summary>
        /// <returns>String message</returns>
        public static string TestStaticMethod()
        {
            return "Hello, you have called the test static method.";
        }
    }

    /// <summary>
    /// Provides PowerShell script execution examples
    /// </summary>
    public class PowerShellExecutor
    {
        /// <summary>
        /// Sample execution scenario 3: Namespace test
        /// </summary>
        /// <remarks>
        /// Executes a PowerShell script synchronously and utilizes classes in the callers namespace.
        /// </remarks>
        public void ExecuteSynchronouslyNamespaceTest()
        {
            using (var powerShellInstance = PowerShell.Create())
            {
                // add a script that creates a new instance of an object from the caller's namespace
                powerShellInstance.AddScript("$t = new-object PowerShellExecutionSample.TestObject;" +
                                             "$t.Name = 'created from inside PowerShell script'; $t;" +
                                             "$message = [PowerShellExecutionSample.TestStaticClass]::TestStaticMethod(); $message");

                // invoke execution on the pipeline (collecting output)
                var psOutput = powerShellInstance.Invoke();

                // loop through each output object item
                foreach (var outputItem in psOutput)
                {
                    if (outputItem != null)
                    {
                        if (outputItem.BaseObject is TestObject)
                        {
                            var testObj = outputItem.BaseObject as TestObject;
                            Console.WriteLine(testObj.Name);
                        }
                    }
                }
            }
        }
    }
}

Open in new window


Here is the debug screen shot:

Execute Powershell script output
Bob LearnedCommented:
You can run the script synchronously with Invoke, instead of asychronously with BeginInvoke.  

If you are adding a cmdlet, there is the PowerShell.AddCommand, instead of AddScript.  

If you have parameters, that would be PowerShell.AddParameter.  

If you want to see if there are errors, there is the PowerShell.HadErrors property
sirbountyAuthor Commented:
Hmm - I was using addscript, but including the script and all it's parameters at once.
I'll investigate the haderrors property - thanks!
sirbountyAuthor Commented:
I never did find that issue with the cast exception, but it 'disappeared' nonetheless (arg, I hate things like that).

But, I've asked a related question here - two minor tweaks I'm looking to add/correct, if you have time.
http://www.experts-exchange.com/Programming/Languages/.NET/Q_28673954.html

Thanks!
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.