Link to home
Start Free TrialLog in
Avatar of sirbounty
sirbountyFlag for United States of America

asked on

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?
Avatar of David Johnson, CD
David Johnson, CD
Flag of Canada image

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)
Avatar of sirbounty

ASKER

It's not so much getting my hands on a copy, it's just hard to get software into the lab...
SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
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

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.
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. :(
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.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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...
Error_DataAdded is not declared.
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.
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());
}
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.
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...
I suspect this goes back to the initial link about the snap-ins not being supported. :(
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?
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)
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?
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. ;^)
Thought it might have to do with this (https://www.experts-exchange.com/questions/28673119/Why-won't-powershell-believe-me.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...
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

Did you take the code that I showed you, and change it beyond the capability for sharing?
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!)
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:

User generated image
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
Hmm - I was using addscript, but including the script and all it's parameters at once.
I'll investigate the haderrors property - thanks!
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.
https://www.experts-exchange.com/questions/28673954/Some-slight-tweaks-on-my-powershell-instance.html

Thanks!