0

I'm attempting to create a function which will take in specified .ps1 file which is embedded within my project, but I'm not quite sure how to change my code around to cater for this.

I've written this in my new .NET 6 C# WPF application based on a VB.net legacy application that does similar stuff:

using System;
using System.Linq;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Windows;

public class PowerShellStuff
{
    private readonly string PS_UserName = "";
    private readonly string PS_Password = "";
    private Runspace? runspace;
    private PowerShell? pipeline;
    public void ConnectToExchange()
    {
        System.Security.SecureString securePassword = new System.Security.SecureString();
        foreach (char c in PS_Password)
        {
            securePassword.AppendChar(c);
        }
        PSCredential? credential = new(PS_UserName, securePassword);
        WSManConnectionInfo? connectionInfo = new(new Uri("https://outlook.office365.com/powershell-liveid/"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential)
        {
            AuthenticationMechanism = AuthenticationMechanism.Basic,
            MaximumConnectionRedirectionCount = 2
        };
        using (runspace = RunspaceFactory.CreateRunspace(connectionInfo))
        {
            using (pipeline = PowerShell.Create())
            {
                runspace.Open();
                pipeline.Runspace = runspace;
            }
        }
    }
    public Collection<PSObject> RunScript(PSCommand command)
    {
        if (runspace == null)
        {
            try
            {
                ConnectToExchange();
                pipeline = PowerShell.Create();
                pipeline.Runspace = runspace;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "User Information", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        if (runspace.RunspaceStateInfo.State != RunspaceState.Opened)
            runspace.Open();

        pipeline.Commands.Clear();
        Command comand = new Command(command.Commands[0].ToString());
        for (int i = 0; i <= command.Commands[0].Parameters.Count - 1; i++)
            comand.Parameters.Add(command.Commands[0].Parameters[i]);

        pipeline.Commands.AddCommand(comand);

        Collection<PSObject> results;
        try
        {
            results = pipeline.Invoke();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "User Information", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        return results;
    }

}

This is how its used in VB.net:

Dim command As New PSCommand
command.AddCommand("").AddParameter("", "")...
RunScript(command)

The issue I am having is that I can't even get the above working in C# as it fails with Non-invocable member 'PSCommand.Commands' cannot be used like a method.

My goal is to have a function which I can use to populate a DataTable with results from the .ps1 script, e.g. DataTable dt = new DataTable(RunScript(Resources.MyScript.ps1)) and a function which will not return any data and just execute a SET command with few parameters, which I imagine would follow the same criteria as the VB.net code with command.AddCommand("").AddParameter("", "")...

Its my first time starting PowerShell in C# from scratch as in the past I only carried out simple changes in existing VB.net code, which I used as base to write this...

EDIT 1:

Clearly C# newbie... thanks to first two commenters the issue of executing the above code is resolved, but still unsure how to execute a .ps1 file using my existing runspace & pipeline and populate a DataTable.

Pawel
  • 141
  • 1
  • 10
  • 4
    Assuming you want to reference the first command, you'll want `Commands[0]`, not `Commands(0)` – Mathias R. Jessen Nov 03 '21 at 14:04
  • 3
    Note that C# uses square brackets `[0]` to access array members by index. – Fixation Nov 03 '21 at 14:05
  • [Execute PowerShell Script from C# with Commandline Arguments](https://stackoverflow.com/questions/527513/execute-powershell-script-from-c-sharp-with-commandline-arguments) – Luuk Nov 07 '21 at 10:30

1 Answers1

0

I was supposed to give you an example here yesterday but ended up necroing another thread that was several years old. Here goes again hehe :)

I struggled getting PS scripts to work through my app as well and made it work just a like a week ago. Here I launch a script on a remote computer to make a Windows Toast Notification to pop up.

I didn't get this script to fully work until I downloaded and installed the PowerShell SDK package through NuGet. Hopefully some of this can get you on the right track.

enter image description here

In the screenshot below you can see I also get the output generated from the script in the Debug window. For some reason Verbose output was not captured even though verbose preference was set in the script. I had to capture the output by putting the statements in the script with quotation marks like this:

"OS Version Display Name: $OsVersion"

enter image description here

using System;
using System.Diagnostics;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;



namespace WindowsToasts
{
 
    public class WindowsToast
    {

        public void Send_WindwsUpdateToast(string computerName)
        {
            InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
            initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;
            using Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
            runspace.Open();

            using PowerShell powerShell = PowerShell.Create(runspace);
         
            string PSPath = @"C:\temp\ToastText.ps1";

            powerShell.AddCommand("Invoke-Command");
            powerShell.AddParameter("ComputerName", computerName);
            powerShell.AddParameter("File", PSPath);

            Collection<PSObject> PSOutput = powerShell.Invoke();
    
            foreach (PSObject outputItem in PSOutput)
            {
                // if null object was dumped to the pipeline during the script then a null object may be present here
                if (outputItem != null)
                {
                    Debug.WriteLine($"Output line: [{outputItem}]");
                }
            }

        }

    }
}
Mogash
  • 130
  • 1
  • 7