take powershell object save as xml and load in c# - c#

I have the following powershell script that scans a location and adds the file details to a xml file,
Get-ChildItem -recurse c:\DATA | Select-Object * , #{Name="Kbytes";Expression={ "{0:N0}" -f ($_.Length / 1Kb) }},#{Name="Age";Expression={ (((Get-Date) - $_.CreationTime).Days) }} | Export-Clixml c:\DATA\Final.xml
As far as I understand this should be a object in the .net framework, assuming a dataset ?
what I would like to do is load this object into a c# application and use it as a dataset.
How would I load the object into a dataset in c# ?

Add reference to the System.Management.Automation.dll assembly.
Create a PowerShell Runspace
Open the Runspace
Create a PowerShell Pipeline object with the Import-CliXml command
Invoke the Pipeline
Close the Runspace
var rs = RunspaceFactory.CreateRunspace();
rs.Open();
var pl = rs.CreatePipeline(#"Import-CliXml c:\DATA\Final.xml;");
var result = pl.Invoke();
rs.Close();

A faster approach might be:
-For lists
object[] xmlAsList =
System.Management.Automation.PSSerializer.DeserializeAsList(System.IO.File.ReadAllText(#"C:\Users\batman\Desktop\myList.clixml"));
-For generic objects
object importedObject =
System.Management.Automation.PSSerializer.Deserialize(System.IO.File.ReadAllText(#"C:\Users\batman\Desktop\myObject.clixml"));
Then, you manage the results via: System.Management.Automation.PSObject using the Members property.

Related

How to catch full invoke text from powershell class in C#

I want to catch the output exactly as I get it when I run commands in PowerShell.
For instance when I type LS, I get:
Yet when I use this code:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create(); // Create a new PowerShell instance
ps.Runspace = runspace; // Add the instance to the runspace
ps.Commands.AddScript("ls"); // Add a script
Collection<PSObject> results = ps.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
Console.WriteLine(obj.ToString());
}
I get the following output:
Microsoft.Management.Infrastructure.dll
System.Management.Automation.dll
System.Management.Automation.xml
WpfApp1.exe
WpfApp1.exe.config
WpfApp1.pdb
Although this output may come in handy, with an other application I dont get the right output in return, so I would prefer the exact output as I see it in PowerShell itself.
Is there a way to read the output as I get it in PowerShell, line for line?
If you want the exact text that powershell produces you then you can use Out-String in the powershell command:
ps.Commands.AddScript("ls | Out-String");
You can also read the values by accessing the Properties of the PSObject:
foreach (PSObject obj in results)
{
var name = obj.Properties["Name"]?.Value.ToString()
var mode = obj.Properties["Mode"]?.Value.ToString();
var length = obj.Properties["Length"]?.Value.ToString();
var lastMod = (DateTime?)obj.Properties["LastWriteTime"]?.Value;
Console.WriteLine(string.Format("{0} {1} {2} {3}", mode, lastMod, length, name));
}
Note, as mentioned in mklement0's answer, you don't need to use Runspace to execute this powershell. Consider using Get-ChildItem rather than ls.
Note: This answer also recommends what part of haldo's helpful answer shows, in a more focused manner and with supplementary information.
Modify your script to pipe your command to the Out-String
cmdlet, which uses PowerShell's formatting system to render to a string, the same way that output renders to the console.
ps.AddScript("ls | Out-String"); // Add a script
Note:
Windows PowerShell assumes a fixed line width of 120 characters and with (implied) tabular (Format-Table) or wide (Format-Wide) formatting, truncates lines that are longer (except if the output object is of type [string]), with the point of truncation indicated with ...
PowerShell [Core] 7+ exhibits the same behavior fundamentally, but only uses default width 120 as a fallback: when the hosting (console-subsystem) executable is running in a console (terminal), the console window's width is used instead, which is the same behavior you get in a regular PowerShell console window (see this answer).
To fix that, pass a large-enough line width to -Width; e.g.:
ps.AddScript("ls | Out-String -Width 200");
Note:
In Windows PowerShell, do not use -Width ([int]::MaxValue-1), because every line is then padded to that width, which will result in excessively large output.
PowerShell [Core] 7+, this padding is no longer performed, and you can safely use
-Width ([int]::MaxValue-1)
A few asides:
For robustness, I suggest avoiding the use of aliases (such as ls for Get-ChildItem) in scripts and compiled code.
In the case at hand, ls wouldn't work on Unix-like platforms, because the alias isn't defined there, so as not to conflict with the platform-native ls utility.
It's best to wrap PowerShell ps = PowerShell.Create(); in a using block to ensure that the PowerShell instance is disposed of: using (PowerShell ps = PowerShell.Create()) { ... }
There is generally no need to create a runspace explicitly - PowerShell.Create() will create one for you.
The System.Management.Automation.PowerShell instance returned by PowerShell.Create() directly exposes methods such as .AddScript() - no need to use the .Commands property.
You can get compressed json output from powershell with this command
ls | ConvertTo-Json -Compress
Then deserialize. Also this command provide extra info than see in powershell output.

Retrieving GetExecutingRequests from PowerShell into c# object

I am trying to retrieve data from PowerShell into a c# object. The data I am looking for is returned from a PowerShell Invoke() of GetExecutingRequests on a remote web server. The issue I'm having is that I am not getting an error code, but the results from the Invoke() that I'm looking for are nowhere in the return data, or on the PowerShell object.
using (Runspace runspace = RunspaceFactory.CreateRunspace(cxn))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
string script = String.Format("Get-WmiObject
WorkerProcess -Namespace root\\WebAdministration -ComputerName {0} |
Invoke-WmiMethod -Name GetExecutingRequests", server);
ps.AddScript(script);
ps.AddParameter("OutputElement", new HttpRequest[0]);
var result = ps.Invoke();
}
}
This code executes, and returns a Collection with 29 items. However, none of them show the GetExecutingRequests results, and there is nothing relevant on the PowerShell object either.
I would like to get the output of GetExecutingRequests into a c# object, so I can do further processing. the PSDataStreams on the ps object also have no results.
Any help would be appreciated.
MORE INFO:
I was able to solve this with a change to the PowerShell script I was sending:
string script = String.Format("Get-WmiObject WorkerProcess -Namespace root\\WebAdministration -ComputerName {0} | Invoke-WmiMethod -Name GetExecutingRequests | %{{ $_ | Select-Object -ExpandProperty OutputElement }}", server);
I'm not quite sure if I can resolve the results directly, but I'd advise running below command to get some some more information on the object being returned. From there, you could look into how you'd handle that returning in C#.
Get-WmiObject -Class $(<scriptblock>) | get-member
Define the object types in C# & see if you're able to capture it in that way first.
If you're not able to make any progress with capturing the object type being returned in powershell yourself, it may be worth posting here to see if anyone else might be able to offer any insight/experience with workarounds for interfacing those objects types into C#.
If above really isn't possible, forcing the powershell to return everything from standard out would help find if it's writing anything meaningful that you can scrape/format in C#. I think the best way to do that in your quoted powershell command would be like so:
return $(Get-WmiObject
WorkerProcess -Namespace root\WebAdministration -ComputerName {0} |
Invoke-WmiMethod -Name GetExecutingRequests | *>&1)
This returns all 5+ standard outs from powershell to the return object (for those reading, see these docs on ps streams). Youshould definitely be able to capture the return in your results variable, but it can't hurt to make sure you're able to throw/capture errors from overflow.
Hope this helps to continue the digging!

Microsoft deployment toolkit from c#

I am trying to use MDT from c# like I can already do with powershell when importing MicrosoftDeploymentToolkit.psd1. For example I can run the command Get-MDTPersistentDrive directly from powershell without problem.
But I can't find a way to do the same thing from c#, I tried to include directly the Microsoft.BDD.PSSnapIn.dll (which was basically what was doing "MicrosoftDeploymentToolkit.psd1") then I could access to the GetPersistent class but an error message informed me that I cannot directly invoke a PSCMDlet.
I then tried to use the PowerShell class
var ps = PowerShell.Create();
ps.AddScript(#"import-module C:\...\MicrosoftDeploymentToolkit.psd1");
ps.Invoke();
ps.AddCommand("Get-MDTPersistentDrive");
var result = ps.Invoke();
But I receive this exception
The term 'Get-MDTPersistentDrive' is not recognized as the name of a
cmdlet, function, script file, or operable program
I then tried to do this
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\...\MicrosoftDeploymentToolkit.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Get-MDTPersistentDrive");
var result= ps.Invoke();
and I receive the error
Object reference not set to an instance of an object
I'm really lost, I don't get what they mean with this error, If you could show me where I'm wrong, or a way to execute PSCmdlet from c# or even better directly how to control MDT that would be awesome.

run powershell from c# application

Hello a have a file with 21 lines of code in power shell. I need a way to run this file with a button in c#. I use c# in Visual Studio 2010. Please let me know if there is a way to achieve.
// Powershell
$Proc = Get-Process | Sort-Object CPU -Descending
$cores = Get-WmiObject Win32_processor
$memory = Get-WmiObject Win32_OperatingSystem
$Total = 10
$num = 1
Clear-Content c:\scripts\prueba.txt
foreach ($objItm in $Proc) {
If ($num -gt $Total) {
break #break the loop
}
[string] $strID=[System.Convert]::ToString($objItm.ID)
[string] $strProcessName=[System.Convert]::ToString($objItm.ProcessName)
[string] $strCPU=[System.Convert]::ToString($objItm.CPU)
[string] $strNUM=[System.Convert]::ToString($num)
$info=$strNUM+"-"+$strID+"-"+$strProcessName+"-"+$strCPU
$num += 1
$info|Add-Content c://scripts/prueba.txt
}
//Code c#
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open(); RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(scriptFile)
Execute PowerShell script results = pipeline.Invoke();
I need for example press a button and make the script happens
You can use PS2EXE by Ingo Karstein
This creates a c# executable with it's own powershell host and with your script embedded.
I have a c# project which takes the c# code in PS2EXE, adds ability to read the contents of an encrypted powershell script, decrypt it and run it.
some tips that works for me . My application is already working
Run Visual Studio with permissions of Administrator this to avoid get a error with the Hkey_Local...
In power shell run this get-executionpolicy to view the policy and set unregistered with set-executionpolicy unregistered. This avoid any restriction by the OS.
And with the code write above the app work. Get ready and test !!!

Passing an associative array from c# to Powershell

I'd like to pass an associative array from C# to Powershell. As an example I'd like to execute this powershell line of code:
PS C:\> get-command | select name, #{N="Foo";E={"Bar"}} -first 3
Name Foo
---- ---
Add-Content Bar
Add-History Bar
Add-Member Bar
I'd like to do this via a Pipeline of distinct Commands as opposed to a single command marked as a script. Here's the code:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add("get-command");
Command c = new Command("select-object");
List properties = new List();
properties.Add("name");
properties.Add("#{N=\"Foo\";E={\"Bar\"}}");
c.Parameters.Add("Property", properties.ToArray());
c.Parameters.Add("First", 3);
pipeline.Commands.Add(c);
pipeline.Commands.Add("Out-String");
Collection retval = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in retval)
Console.WriteLine(obj.ToString());
But that associative array being passed in as a parameter to Select-Object isn't being parsed correctly. This is what comes out the other side:
PS C:\test> c:\test\Bin\Debug\test.exe
Name #{N="Foo";E={"Bar"}}
---- --------------------
Add-Content
Add-History
Add-Member
What's wrong with how I'm setting up the Select-Object command parameters?
Creating a pipeline through c# and creating a pipeline with native powershell script have one major difference that is actually quite subtle: the parameter binder.
if I write a version of your code in pure script, I will get the same error: the hashtable literal is treated as a string value.
ps> $ps = $ps.Commands.Add("get-process")
ps> $ps = $ps.Commands.Add("select-object")
ps> $ps.Commands[1].Parameters.Add("Property", #("Name", '#{N="Foo";E={"Bar"}}'))
In this case, the command receives an array of two strings, the "name" and the hashtable literal string. This will be broken in exactly the same way as your C#. Now, take a look at the right way to do it (in script) - let me rewrite line 3:
ps> $ps.Commands[1].Parameters.Add("Property", #("Name", #{N="Foo";E={"Bar"}}))
So what changed? I removed the quotes around the hashtable -- I am passing a hashtable as the 2nd element of the object array! So, to get your C# example to work, you need to do what the parameter binder does for us at the command line (which is quite a lot!). Replace:
properties.Add("#{N=\"Foo\";E={\"Bar\"}}");
with
properties.Add(
new Hashtable {
{"N", "Foo"},
{"E", System.Mananagement.Automation.ScriptBlock.Create("\"Foo\"")}
}
);
I hope this clears it up for you. The parameter binder is probably the least visible but most powerful part of the powershell experience.
-Oisin

Categories

Resources