Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Executing PowerShell code at the API level
#1
Background:
Compared to BAT and VBS, PowerShell code is simpler and more understandable, making it very beginner-friendly for programming. Additionally, all Microsoft products have PowerShell interfaces, and there are many powerful open-source libraries available.(https://www.powershellgallery.com/)
Many times, I've encountered various issues when trying to convert PowerShell code to C# using ChatGPT. This conversion often doesn't provide significant value.
 
I have been using the method described in the link below to execute PowerShell code, but it's too slow.
https://www.libreautomate.com/cookbook/P...ython.html
 
I found a method in the link below for executing PowerShell code at the API level.
https://github.com/p3nt4/PowerShdll
 
Is it possible to create a similar class in LA?
 
It would be great if seamless switching and using variables were possible.
For example, the variable name "var1" in C# and "$var1" in PowerShell represent the same variable, with smooth input and output.
I believe this is achievable, perhaps requiring more programming skills.

C# + PowerShell is simply unbeatable. Tongue

E.g:

string var1="""
hello
world
""";

string var2="test.txt";
    
var R = ps.run("""
($var1 -split '\r?\n') -join '|' > $HOME\Desktop\$var2
'hello world'
""");

print.it($"Res: {R}");
#2
https://www.nuget.org/packages/Microsoft.PowerShell.SDK

2 examples.

Code:
Copy      Help
// script "nuget PowerShell.cs"
/*/ nuget ps\Microsoft.PowerShell.SDK; /*/
using System.Management.Automation;

PS();

void PS() {
    var psCode = """
    & 'C:\Program Files\Windows NT\Accessories\wordpad.exe'
    """
;

    using var ps = PowerShell.Create();
    ps.AddScript(psCode);
    ps.Invoke();
}

Code:
Copy      Help
// script "PowerShell2.cs"
/*/ nuget ps\Microsoft.PowerShell.SDK; /*/
using System.Management.Automation;

print.clear();

string code = """
Get-Process | out-string
"""
;

var results = PS.Invoke(code);
foreach (var result in results) {
    print.it(result.ToString());
}


static class PS {
    public static System.Collections.ObjectModel.Collection<PSObject> Invoke(string command) {
        using (var ps = PowerShell.Create()) {
            ps.AddScript(command);
            
            var results = ps.Invoke();
            
            // report non-runspace-terminating errors, if any.
            foreach (var error in ps.Streams.Error) {
                print.it("ERROR: " + error.ToString());
            }

            
            return results;
        }
    }
}
#3
Thank you for sharing.

Using the SDK requires downloading a large number of files, and the execution speed hasn't improved.

I put the PowerShdll source code into the LA and only referenced the System.Management.Automation.dll from the PowerShell 7 installation folder. This allowed me to execute PowerShell code without any additional dependencies, and the execution speed improved significantly.
#4
Does not work here.
Quote:System.Management.Automation.Runspaces.PSSnapInException: Cannot load PowerShell snap-in Microsoft.PowerShell.Diagnostics because of the following error: The PowerShell snap-in module C:\Program Files\PowerShell\7\Microsoft.PowerShell.Commands.Diagnostics.dll does not have the required PowerShell snap-in strong name Microsoft.PowerShell.Commands.Diagnostics, Version=7.2.5.500, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=MSIL.

 
#5
Set EXE outputPath %folders.ProgramFiles%\PowerShell\7

/*/ role exeProgram; outputPath %folders.ProgramFiles%\PowerShell\7; r %folders.ProgramFiles%\PowerShell\7\System.Management.Automation.dll; /*/
Furthermore, if the generated .exe is not located in the PowerShell 7 installation folder, how can we call the DLLs located in that folder?

Line 326 public class PS
https://github.com/p3nt4/PowerShdll/blob...on.cs#L326

The speed of executing PowerShell code at the API level is acceptable, and it's faster than executing Python.
So, I am looking forward to the following features. The data types in C# and PowerShell are compatible, which is much more convenient than using Python
-----------------------------------------
string var1="""
hello
world
""";

string var2="test.txt";
    
var R = ps.run("""
($var1 -split '\r?\n') -join '|' > $HOME\Desktop\$var2
'hello world'
""");

print.it($"Res: {R}");
#6
Parse variable names and variable values in the Powershell code
 
Code:
Copy      Help
// script "PS_variable values.cs"
/*/ r %folders.ProgramFiles%\PowerShell\7\System.Management.Automation.dll; /*/ //.
using System.Management.Automation.Language;
script.setup(trayIcon: true, sleepExit: true);
//..

string s = """
$Hash = @{
    Path       = "D:\\c.txt"
    Encoding   = 'UTF8'
}
$string = "hello"
$int = 888
"""
;

Token[] tokens;
ParseError[] errors;
var parsedCode = Parser.ParseInput(s, out tokens, out errors);

foreach (var ast in parsedCode.FindAll(astItem => astItem is AssignmentStatementAst, true))
{

    var assignmentAst = (AssignmentStatementAst)ast;
    if (assignmentAst.Left is VariableExpressionAst variableExpressionAst)
    {

        var variableName = variableExpressionAst.VariablePath.UserPath;
        var variableValueAst = assignmentAst.Right;
        var variableValue = variableValueAst.Extent.Text.Trim();
        Console.WriteLine($"variableName: {variableName}");
        Console.WriteLine($"variableValue: {variableValue}");
    }
}
#7
After compiling and generating the EXE, some DLL files(Local: C:\Program Files\PowerShell\7) are always missing in the directory where the EXE file is located. For example:

- Microsoft.PowerShell.Commands.Diagnostics.dll
- Microsoft.PowerShell.Commands.Management.dll
...

How can I ensure that these DLLs are always copied when generating the EXE? Is this possible?
#8
Code:
Copy      Help
/*/ r file1.dll; r file2.dll; ... /*/
#9
Another way. Not tested with PowerShell dlls.
 
Code:
Copy      Help
using System.Runtime.Loader;

AssemblyLoadContext.Default.Resolving += (o, e) => {
    //print.it(e.Name);
    var path = $@"C:\Program Files\PowerShell\7\{e.Name}.dll";
    if (filesystem.exists(path)) return AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
    return null;
};

It will not copy dlls to the exe folder, but will use dlls in the PowerShell folder.
#10
The above code is useful, thank you!


Forum Jump:


Users browsing this thread: 1 Guest(s)