C
C#2y ago
Sir Ruggie

✅ PowerShell Method in WinUI C#

So firstly, yes I have to use Powershell and am unable to just do it in C#. Below will be an example of a basic PowerShell script that I have My issue is that I'm returning a System.Data.Datarow, but I'm unable to return any data in C#. PowerShell Console Results
PS R:\> \\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124

POS POSNumber
--- ---------
POS 1 - Cashier 01
POS 2 - Takeout 02
POS 3 - Takeout 03
POS 4 - Bar 04
POS 5 - Takeout 05
POS 6 - Takeout 06
POS 7 - Takeout 07
POS 8 - Takeout 08


PS R:\> \\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124 |GM


TypeName: System.Data.DataRow

Name MemberType Definition
---- ---------- ----------
AcceptChanges Method void AcceptChanges()
BeginEdit Method void BeginEdit()
CancelEdit Method void CancelEdit()
ClearErrors Method void ClearErrors()
Delete Method void Delete()
EndEdit Method void EndEdit()
Equals Method bool Equals(System.Object obj)
GetChildRows Method System.Data.DataRow[] GetChildRows(string relationName), System.Data.DataRow[] GetChildRows(string relationName, ...
GetColumnError Method string GetColumnError(int columnIndex), string GetColumnError(string columnName), string GetColumnError(System.Da...
GetColumnsInError Method System.Data.DataColumn[] GetColumnsInError()
GetHashCode Method int GetHashCode()
GetParentRow Method System.Data.DataRow GetParentRow(string relationName), System.Data.DataRow GetParentRow(string relationName, Syst...
GetParentRows Method System.Data.DataRow[] GetParentRows(string relationName), System.Data.DataRow[] GetParentRows(string relationName...
GetType Method type GetType()
HasVersion Method bool HasVersion(System.Data.DataRowVersion version)
IsNull Method bool IsNull(int columnIndex), bool IsNull(string columnName), bool IsNull(System.Data.DataColumn column), bool Is...
RejectChanges Method void RejectChanges()
SetAdded Method void SetAdded()
SetColumnError Method void SetColumnError(int columnIndex, string error), void SetColumnError(string columnName, string error), void Se...
SetModified Method void SetModified()
SetParentRow Method void SetParentRow(System.Data.DataRow parentRow), void SetParentRow(System.Data.DataRow parentRow, System.Data.Da...
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(int columnIndex) {get;set;}, System.Object Item(string columnName) {get;set;}, System.Object I...
POS Property string POS {get;set;}
POSNumber Property string POSNumber {get;set;}
PS R:\> \\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124

POS POSNumber
--- ---------
POS 1 - Cashier 01
POS 2 - Takeout 02
POS 3 - Takeout 03
POS 4 - Bar 04
POS 5 - Takeout 05
POS 6 - Takeout 06
POS 7 - Takeout 07
POS 8 - Takeout 08


PS R:\> \\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124 |GM


TypeName: System.Data.DataRow

Name MemberType Definition
---- ---------- ----------
AcceptChanges Method void AcceptChanges()
BeginEdit Method void BeginEdit()
CancelEdit Method void CancelEdit()
ClearErrors Method void ClearErrors()
Delete Method void Delete()
EndEdit Method void EndEdit()
Equals Method bool Equals(System.Object obj)
GetChildRows Method System.Data.DataRow[] GetChildRows(string relationName), System.Data.DataRow[] GetChildRows(string relationName, ...
GetColumnError Method string GetColumnError(int columnIndex), string GetColumnError(string columnName), string GetColumnError(System.Da...
GetColumnsInError Method System.Data.DataColumn[] GetColumnsInError()
GetHashCode Method int GetHashCode()
GetParentRow Method System.Data.DataRow GetParentRow(string relationName), System.Data.DataRow GetParentRow(string relationName, Syst...
GetParentRows Method System.Data.DataRow[] GetParentRows(string relationName), System.Data.DataRow[] GetParentRows(string relationName...
GetType Method type GetType()
HasVersion Method bool HasVersion(System.Data.DataRowVersion version)
IsNull Method bool IsNull(int columnIndex), bool IsNull(string columnName), bool IsNull(System.Data.DataColumn column), bool Is...
RejectChanges Method void RejectChanges()
SetAdded Method void SetAdded()
SetColumnError Method void SetColumnError(int columnIndex, string error), void SetColumnError(string columnName, string error), void Se...
SetModified Method void SetModified()
SetParentRow Method void SetParentRow(System.Data.DataRow parentRow), void SetParentRow(System.Data.DataRow parentRow, System.Data.Da...
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(int columnIndex) {get;set;}, System.Object Item(string columnName) {get;set;}, System.Object I...
POS Property string POS {get;set;}
POSNumber Property string POSNumber {get;set;}
Within my application I have a PowerShellHelper.cs where I'm trying to use System.Management.Automation with the below method
public static Collection<PSObject> GetTableData(string script)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);


Collection<PSObject> results = powerShellInstance.Invoke();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
}
else
{
Debug.WriteLine("Results: " + results.ToString());
}

return results;
}
}
public static Collection<PSObject> GetTableData(string script)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);


Collection<PSObject> results = powerShellInstance.Invoke();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
}
else
{
Debug.WriteLine("Results: " + results.ToString());
}

return results;
}
}
35 Replies
Sir Ruggie
Sir RuggieOP2y ago
Also tried
public static DataTable GetTableData(string script)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);

powerShellInstance.Streams.Verbose.DataAdded += (sender, e) =>
{
Debug.WriteLine(powerShellInstance.Streams.Verbose[e.Index].Message);
};

try
{
Collection<DataRow> results = powerShellInstance.Invoke<DataRow>();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
return null;
}

DataTable table = new DataTable();
table.Columns.Add("POS");
table.Columns.Add("POSNumber");

foreach (DataRow row in results)
{
DataRow newRow = table.NewRow();
newRow["POS"] = row["POS"];
newRow["POSNumber"] = row["POSNumber"];
table.Rows.Add(newRow);
}

return table;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
public static DataTable GetTableData(string script)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);

powerShellInstance.Streams.Verbose.DataAdded += (sender, e) =>
{
Debug.WriteLine(powerShellInstance.Streams.Verbose[e.Index].Message);
};

try
{
Collection<DataRow> results = powerShellInstance.Invoke<DataRow>();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
return null;
}

DataTable table = new DataTable();
table.Columns.Add("POS");
table.Columns.Add("POSNumber");

foreach (DataRow row in results)
{
DataRow newRow = table.NewRow();
newRow["POS"] = row["POS"];
newRow["POSNumber"] = row["POSNumber"];
table.Rows.Add(newRow);
}

return table;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
and in my class that is calling it, I'm calling it something like
private void LoadPOSData()
{
string scriptPath = @"\\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124";
DataTable results = PowerShellHelper.GetTableData(scriptPath);

if (results == null || results.Rows.Count == 0)
{
Debug.WriteLine("No data returned from the PowerShell script.");
return;
}

foreach (DataRow result in results.Rows)
{
Debug.WriteLine(result["POS"].ToString() + " - " + result["POSNumber"].ToString());
}
}
private void LoadPOSData()
{
string scriptPath = @"\\ris1\xnet\data\Wan\RestDB_Dependencies\PowerShell_Scripts\POS\Get-POS.ps1 -Site 1124";
DataTable results = PowerShellHelper.GetTableData(scriptPath);

if (results == null || results.Rows.Count == 0)
{
Debug.WriteLine("No data returned from the PowerShell script.");
return;
}

foreach (DataRow result in results.Rows)
{
Debug.WriteLine(result["POS"].ToString() + " - " + result["POSNumber"].ToString());
}
}
lycian
lycian2y ago
I was able to get results with a simple test:
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Management.Automation;


var data = GetData();
var psObject = data.Single();
var serialized = JsonConvert.SerializeObject(psObject.Properties.ToDictionary(k => k.Name, v => v.Value));
Console.WriteLine(serialized);

static Collection<PSObject> GetData()
{
var script = @"C:\repos\psexample\Script.ps1";

using (var powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);

Collection<PSObject> results = powerShellInstance.Invoke();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
}
else
{
Debug.WriteLine("Results: " + results.ToString());
}

return results;
}
}
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Management.Automation;


var data = GetData();
var psObject = data.Single();
var serialized = JsonConvert.SerializeObject(psObject.Properties.ToDictionary(k => k.Name, v => v.Value));
Console.WriteLine(serialized);

static Collection<PSObject> GetData()
{
var script = @"C:\repos\psexample\Script.ps1";

using (var powerShellInstance = PowerShell.Create())
{
Debug.WriteLine("Script: " + script);
powerShellInstance.AddScript(script);

Collection<PSObject> results = powerShellInstance.Invoke();

if (results == null || results.Count == 0)
{
Debug.WriteLine("No results returned.");
}
else
{
Debug.WriteLine("Results: " + results.ToString());
}

return results;
}
}
and the script:
$dt = New-Object -TypeName System.Data.DataTable -ArgumentList "MyDataTable"

$dc = New-Object -TypeName System.Data.DataColumn
$dc.DataType = "".GetType()
$dc.ColumnName = "Column 1"

$dt.Columns.Add($dc)

$dr = $dt.NewRow()
$dr[$dc] = "Test"

return $dr
$dt = New-Object -TypeName System.Data.DataTable -ArgumentList "MyDataTable"

$dc = New-Object -TypeName System.Data.DataColumn
$dc.DataType = "".GetType()
$dc.ColumnName = "Column 1"

$dt.Columns.Add($dc)

$dr = $dt.NewRow()
$dr[$dc] = "Test"

return $dr
Omnissiah
Omnissiah2y ago
i think i would use json too
Sir Ruggie
Sir RuggieOP2y ago
This thing is kicking my butt.. It will not display anything with the above data at all using Json, but it will return something with this, but then it's just a big jumbled together mess of string.
public static string RunPowerShellCommand(string command)
{
// Create a new process for PowerShell
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "powershell.exe";

// Pass the command to the new PowerShell process
startInfo.Arguments = $"-Command \"& {command}\"";

// Redirect the output of the command to a stream
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;

// Start the new PowerShell process
using (Process process = Process.Start(startInfo))
{
// Read the output of the command
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
return result;
}
}
}
public static string RunPowerShellCommand(string command)
{
// Create a new process for PowerShell
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "powershell.exe";

// Pass the command to the new PowerShell process
startInfo.Arguments = $"-Command \"& {command}\"";

// Redirect the output of the command to a stream
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;

// Start the new PowerShell process
using (Process process = Process.Start(startInfo))
{
// Read the output of the command
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
return result;
}
}
}
I am using WinUI 3.0 Do you think that would make it any different?
Omnissiah
Omnissiah2y ago
you are not serializing the data try piping the datatable to ConvertTo-Json or probably better, first make a list of your object like @{} with all the fields, the json, the send it to your app
lycian
lycian2y ago
the json usage was just because "toString" on DataRow just outputs the type. You shouldn't need JSON here, the object was visible in the debugger without it
Sir Ruggie
Sir RuggieOP2y ago
For some reason I'm not able to see any data... I am using the same code you're using and I made a new WIN UI project just to make it fresh to test it and it keeps coming back as null data but I can run the script from powershell and return the data
lycian
lycian2y ago
I don't think WinUI will cause any issue, but have you tried with just a commandline app just in case? othwerwise, the next thing to check is that this returns anything Collection<PSObject> results = powerShellInstance.Invoke(); If it does, the rest is just failing because of conversion (likely)
Sir Ruggie
Sir RuggieOP2y ago
So this is the result that I'm getting by using this
using Microsoft.UI.Xaml;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;

var script = @"C:\Temp\Test.ps1";
PowerShell powerShellInstance = PowerShell.Create();
powerShellInstance.AddScript(script);
Collection<PSObject> results = powerShellInstance.Invoke();
using Microsoft.UI.Xaml;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;

var script = @"C:\Temp\Test.ps1";
PowerShell powerShellInstance = PowerShell.Create();
powerShellInstance.AddScript(script);
Collection<PSObject> results = powerShellInstance.Invoke();
lycian
lycian2y ago
what's in test.ps1?
Sir Ruggie
Sir RuggieOP2y ago
Just that one column 1 It's the script you sent above
$dt = New-Object -TypeName System.Data.DataTable -ArgumentList "MyDataTable"

$dc = New-Object -TypeName System.Data.DataColumn
$dc.DataType = "".GetType()
$dc.ColumnName = "Column 1"

$dt.Columns.Add($dc)

$dr = $dt.NewRow()
$dr[$dc] = "Test"

return $dr
$dt = New-Object -TypeName System.Data.DataTable -ArgumentList "MyDataTable"

$dc = New-Object -TypeName System.Data.DataColumn
$dc.DataType = "".GetType()
$dc.ColumnName = "Column 1"

$dt.Columns.Add($dc)

$dr = $dt.NewRow()
$dr[$dc] = "Test"

return $dr
lycian
lycian2y ago
interesting... what version of .NET are you using? and packages?
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.2" />
<PackageReference Include="System.Management.Automation" Version="7.3.2" />
</ItemGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.2" />
<PackageReference Include="System.Management.Automation" Version="7.3.2" />
</ItemGroup>

</Project>
is my csproj
lycian
lycian2y ago
is what I see in the debugger
Sir Ruggie
Sir RuggieOP2y ago
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>TableTest</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.5" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>TableTest</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.5" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
So maybe I need to reference .NET 7
lycian
lycian2y ago
idk, but I'd reduce differences until we figure it out
Sir Ruggie
Sir RuggieOP2y ago
ok, changing now...
lycian
lycian2y ago
I also wonder if there's some issue with script execution policy... if the script isn't returning anything that could be it? does it have errors on invocation?
Sir Ruggie
Sir RuggieOP2y ago
I always see this error when changing The project 'TableTest' ran into a problem during the last operation: A numeric comparison was attempted on "$(TargetPlatformVersion)" that evaluates to "" instead of a number, in condition "'$(TargetPlatformVersion)' < '10.0.18362.0'". C:\Users\gposab7.nuget\packages\microsoft.windowsappsdk\1.2.221109.1\buildTransitive\Microsoft.InteractiveExperiences.Common.targets You may need to reload the solution after fixing the problem. Execution policy is Bypass
lycian
lycian2y ago
just try a new console project, not winui
Sir Ruggie
Sir RuggieOP2y ago
ok
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.2" />
<PackageReference Include="System.Management.Automation" Version="7.3.2" />
</ItemGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.2" />
<PackageReference Include="System.Management.Automation" Version="7.3.2" />
</ItemGroup>

</Project>
That matches now
Sir Ruggie
Sir RuggieOP2y ago
lycian
lycian2y ago
is there anything in the error stream of the psinstance?
Sir Ruggie
Sir RuggieOP2y ago
{System.Management.Automation.PSDataCollection<System.Management.Automation.ErrorRecord>}
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Management.Automation;

var script = @"C:\Temp\Test.ps1";
PowerShell powerShellInstance = PowerShell.Create();
powerShellInstance.AddScript(script);
Collection<PSObject> results = powerShellInstance.Invoke();

var errorStream = powerShellInstance.Streams.Error;
if (errorStream.Count > 0)
{
foreach (var error in errorStream)
{
Console.WriteLine(error.ToString());
}
}

foreach (var PSObject in results)
{
var serialized = JsonConvert.SerializeObject(PSObject.Properties.ToDictionary(k => k.Name, v => v.Value));
Console.WriteLine(serialized);
}
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Management.Automation;

var script = @"C:\Temp\Test.ps1";
PowerShell powerShellInstance = PowerShell.Create();
powerShellInstance.AddScript(script);
Collection<PSObject> results = powerShellInstance.Invoke();

var errorStream = powerShellInstance.Streams.Error;
if (errorStream.Count > 0)
{
foreach (var error in errorStream)
{
Console.WriteLine(error.ToString());
}
}

foreach (var PSObject in results)
{
var serialized = JsonConvert.SerializeObject(PSObject.Properties.ToDictionary(k => k.Name, v => v.Value));
Console.WriteLine(serialized);
}
lycian
lycian2y ago
does it have errors in the error stream?
Sir Ruggie
Sir RuggieOP2y ago
Sir Ruggie
Sir RuggieOP2y ago
Is that what you were looking for?
lycian
lycian2y ago
it will be null on that line because it's not initialized yet. Look at powerShellInstance.Streams.Error when broken on a line, the line hasn't been executed yet
Sir Ruggie
Sir RuggieOP2y ago
Sir Ruggie
Sir RuggieOP2y ago
found the error
Sir Ruggie
Sir RuggieOP2y ago
But I can run the script outside of this
lycian
lycian2y ago
try setting execution policy to unrestricted? I'm not quite a powershell expert, but policy is definitely the problem here
Sir Ruggie
Sir RuggieOP2y ago
Alright, I will have to try this on a non work laptop as I don't have permissions to change policy. I'll let you know, thank you!
lycian
lycian2y ago
you might be able to do
var sessionState = InitialSessionState.CreateDefault(); sessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; PowerShell powerShellInstance = PowerShell.Create(sessionState);
but it's beyond my expertise at that point, just a guess based on API structure 🙂 I know you can invoke ps with a different execution policy than the default
Sir Ruggie
Sir RuggieOP2y ago
That worked! even though my execution policy was set to unrestricted it still complained, idk y
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server