Description of Issue:
I've created a host for Powershell 2.0. The host uses WPF and C#.
The problem is, much output to my WPF host is not formatted the same way as what is displayed when using Powershell ISE.
When I run the script ls alias: in Powershell ISE, the results are in a table form, with CommandType, Name, and Definition column headers. When I run the same script in my WPF host, the results are simply a list of the names of aliases. A similar issue happens with many other scripts, commandlets, and aliases.
Question:
How do I get the same formatting functionality that seems to be built into Powershell ISE? Is this an issue with the Powershell side of things, or do I need to include extra code on the WPF side of things?
Code Sample:
Here is the class I've built to handle Powershell input and output, from within WPF (framework courtesy of example at code.msdn.microsoft.com, although my class is not asynchronous):
internal class PSInterfacer
{
private Runspace runspace { get; set; }
public PSInterfacer()
{
runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
}
public string RunScript(string script)
{
string tempOut = "";
// create powershell instance
using (PowerShell ps = PowerShell.Create())
{
// add existing runspace
ps.Runspace = runspace;
// add script
ps.AddScript(script);
// pipe errors to the same output as normal results
ps.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error,
PipelineResultTypes.Output);
// put output in list
List<PSObject> psOutputs = ps.Invoke().ToList();
// display output elements, seperated by newlines
foreach (PSObject psObject in psOutputs)
{
if (psObject != null)
{
tempOut += psObject.BaseObject;
}
if (psObject != psOutputs.Last())
{
tempOut += Environment.NewLine;
}
}
return tempOut;
}
}
}
After working for a few days, found this solution. Basically, use the .AddCommand method (in the Powershell class) to pipe to "Out-String." That formats everything properly.
Replace RunScript method with this:
public string RunScript(string script)
{
string tempOut = "";
try
{
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript(script);
ps.AddCommand("Out-String");
ps.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
List<PSObject> psOutputs = ps.Invoke().ToList();
foreach (PSObject psObject in psOutputs)
{
if (psObject != null)
{
tempOut += psObject.BaseObject;
}
}
}
}
catch(Exception ex)
{
tempOut = string.Format("Error when invoking powershell commands:\n{0}", ex.Message);
tempOut += "\nThis function did not complete.";
}
return tempOut;
}
Related
I have a windows service (C#) running as Local System.
I want to be able to read my database and run PowerShell commands and scripts.
I am able to run most scripts but my test machine is hanging on this one :
NET USE Z: /Delete /y
NET USE Z: \\TEST2\ProgramData
I can run these commands on the computer and it all works but when I try to run these commands from within my Windows Service it hands on the line which runs the script.
private static bool RunPSCommand(string command, out string output)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(command);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
try
{
StringBuilder stringBuilder = new StringBuilder();
Collection<PSObject> results = pipeline.Invoke();
if (pipeline.HadErrors)
{
var errors = pipeline.Error.ReadToEnd();
foreach (object error in errors)
{
stringBuilder.AppendLine(error.ToString());
}
}
// close the runspace
runspace.Close();
// convert the script result into a single string
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
output = stringBuilder.ToString();
return true;
}
catch (CommandNotFoundException e)
{
output = e.Message;
return false;
}
catch (Exception e)
{
output = e.Message;
return false;
}
}
I am not sure why this is so difficult. I have been wracking my head on this one for three days and trying every option from net use to DOM objects
NET USE Z: /Delete /y
(New-Object -Com WScript.Network).MapNetworkDrive("z:" , "\\test2\programdata")
I was attempting to do this from a service but switched to an application running on a user session. It works this way.
I am trying to get a list of all Virtual Processes started by Microsoft AppV using C#.
I tried using Powershell in C# but I get this error:
System.Management.Automation.CommandNotFoundException: 'The 'Get-AppvVirtualProcess' command was found in the module 'AppvClient', but the module could not be loaded. For more information, run 'Import-Module AppvClient'.'
The weird thing is that if I use the Powershell command line, it works just fine and lists the virtual processes.
So in C# I did a:
ps.Commands.AddCommand("Get-Command");
and it shows Get-AppvVirtualProcess listed as a command:
The result:
Function Get-AppvVirtualProcess 1.0.0.0 A
I tried loading the module in C# manually using:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
and
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
But it still gives me the same error mentioned above.
The code in C# looks like this:
public static void powershellCommand()
{
Collection<PSObject> result;
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace())
{
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
ps.Commands.AddCommand("Get-AppvVirtualProcess");
result = ps.Invoke();
var builder = new StringBuilder();
foreach (PSObject psObject in result)
{
builder.Append(psObject.ToString() + "\n");
builder.ToString();
}
Console.WriteLine("Virtual Process: {0}", builder.ToString());
}
}
Instead of Runspace, I tried this as well but I get the same error:
public static void p()
{
using (var powershell = PowerShell.Create())
{
powershell.AddCommand("Get-AppvVirtualProcess");
powershell.Invoke();
}
}
You could try to iterate through all the running process, and find those that loaded either AppVEntSubsystems32.dll or AppVEntSubsystems64.dll.
You can read more about this here: https://blogs.msdn.microsoft.com/gladiator/2014/09/04/app-v-5-on-application-launch/
I was trying to invoke powershell commands in a c# .net environment and manipulate the outputs, for example: I would like to iterate through each parent and child nodes and get their Innertext value and map them into my classes, but all I get is the whole xml content: below is my code so far:
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
Pipeline pipe = rs.CreatePipeline();
//create powershell
using (PowerShell powershell = PowerShell.Create())
{
//Add the commnads to the PowerShell object.
powershell.AddCommand("Get-Content").AddArgument("C:\\XMLFileSettings.xml");
try
{
foreach (PSObject result in powershell.Invoke())
{
Console.WriteLine("{0}", result.ToString());
}
}
finally
{
}
}
I want to create a powershell function and use it from inside the C#
using System;
using System.Management.Automation;
using System.Text;
namespace PowerShell_eg
{
public class Program
{
public static void Main(String[] args)
{
var psFunction = #" function Get-Hostname { hostname } ";
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
??// HOW TO Add Get-Hostname & INVOKE IT ??
pipeline.Commands.AddScript(psFunction);
pipeline.Commands.Add("Get-Hostname");
var results = pipeline.Invoke();
foreach (var obj in results.Where(o => o != null))
{
Console.WriteLine("\t" + obj);
}
}
}
}
Currently I get CommandNotFound exception # Invoke.
The term 'Get-Hostname' is not recognized as the name of a cmdlet, function, script file, or operable program.
Please advice how to correctly do this!
Also it will be ideal if I can add multiple functions and cont. to use them over the life of the powershell session without having to add them again and again.
This C# code seems to work fine for me. Just add a reference to the System.Management.Automation .NET assembly.
using System;
using System.Management.Automation;
namespace PowerShellTest02
{
class Program
{
static void Main(string[] args)
{
string func = #"function Test { Write-Host 'hello' };";
PowerShell ps = PowerShell.Create();
ps.AddScript(func);
ps.Invoke();
ps.AddCommand("Test");
ps.Invoke();
Console.WriteLine("Successfully executed function");
Console.ReadLine();
}
}
}
I am writing a c# Windows Form app to migrate Exchange 2010 mailboxes to a file location on the server in .pst format. I used an example from the Powershell SDK (Runspace05) to access the exchange cmdlets (Get-Mailbox) and populate a combo-box with the users mailboxes with no problem.
The parts i'm having trouble with is getting the New-MailboxExportRequest cmdlet (the cmdlet that performs the export) to run and the ability to return it's objects and show them in a listbox control. What am I missing? Thanks in advance for your help.
The Code:
InitialSessionState iss = InitialSessionState.CreateDefault();
PSSnapInException warning;
iss.ImportPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out warning);
using (Runspace myrunspace = RunspaceFactory.CreateRunspace(iss))
{
myrunspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
var mailbox = cmbEmailUserName.Text;
var pstFile = txtFileSaveLocation.Text;
const int badLimit = 100; //can be increased in necessary
powershell.AddCommand("Microsoft.Exchange.Management.PowerShell.E2010\\New-MailboxExportRequest");
powershell.AddParameter("Mailbox", mailbox);
powershell.AddParameter("FilePath", pstFile);
powershell.Runspace = myrunspace;
Collection<PSObject> results = powershell.Invoke();
foreach (PSObject thisResult in results)
{
lstBoxStatus.Items.Add(thisResult);
}
myrunspace.Close();
}
}
You want to access the properties of the PSObject, not the object itself.
Try this:
foreach (PSObject thisResult in results)
{
foreach (PSPropertyInfo thisResultInfo in thisResult.Properties)
{
lstBoxStatus.Items.Add("Name: {0} Value: {1} Type: {2}", thisResultInfo.Name, thisResultInfo.Value, thisResultInfo.MemberType);
}
}