Run CSharp code to powershell and use of 3rd party DLL

Costas

Administrator
Staff member
To find the version on powershell exists to the system execute
$PSVersionTable

Bash:
#befine params, the script accept parameters and we made it mandatory
param(
 [Parameter(Mandatory = $true)]
 [string]$inputfile,
 [Parameter(Mandatory = $true)]
 [int]$cutOffset
)


if (![System.IO.File]::Exists($inputfile)) {
    Write-Warning "file does not exists"
    exit
} elseif ($cutOffset -lt 1) {
    Write-Warning "please enter a positive number"
    exit
}

$code = @"
using System.IO;
using System.Text;
using System.Windows.Forms;

public class General
{
    public static string RemoveXfirstChars(string filename, int valCut)
    {
        StringBuilder sb = new StringBuilder();

        string fl = filename;
        string flNew = filename + "_new.txt";

        string line = string.Empty;
        using (StreamReader file = new StreamReader(fl))
        {
            while ((line = file.ReadLine()) != null)
            {
                if (line != null)
                    sb.AppendLine(line.Remove(0, valCut));
            }
        }

        File.WriteAllText(flNew, sb.ToString());

        MessageBox.Show("done!");
 
        return flNew;
    }
}
"@

#load the C# code TYPE - and reference the Winforms ONLY to C# code TYPE no to PS script
#the if sometimes needed, as by default the PS holding the type loaded for the current session (aka till close the PS window)
if ("General" -as [type]) {} else {
    Add-Type -TypeDefinition $code -ReferencedAssemblies System.Windows.Forms
}

try {
    $fileNew = [General]::RemoveXfirstChars($inputfile, $cutOffset)
    Write-Host "$fileNew successfully generated!" -ForegroundColor Green
}
catch { Write-Error "Ran into an issue: $PSItem" }

Microsoft - Call native Windows APIs | Add a .NET type to a session
Microsoft - Parameters Guide
Microsoft - Comparison Operators
How To Format Write-Host With Multiple Colors
(2023) ironmansoftware/code-conversion net6
(2020) Use DLL files in powershell
(2016) Running WPF from a PowerShell
Load assembly as binary stored as base64 into ps1 (LOL) - C# Tools to PowerShell



Microsoft - Add type to powershell script

There are many away to reference a DLL to powershell script, the below has nothing to do with the custom C# CODE, reference sample posted above^, is the only way to reference DLL to C# code :
C#:
//source - https://blog.ironmansoftware.com/daily-powershell-2/
Add-Type -AssemblyName 'System.Windows.Forms'
Add-Type -Path 'C:\src\universal\src\output\Universal.Cmdlets.dll'
[System.Reflection.Assembly]::LoadFrom('C:\src\universal\src\output\Universal.Cmdlets.dll')
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
Import-Module 'C:\src\universal\src\output\Universal.Cmdlets.dll'
using assembly C:\src\universal\src\output\Universal.Cmdlets.dll



Doing tests with references, oops look at this. someone creates temporary filenames ;)

Ib2rRaY.png



here is a quick & dirty example on how to load a 3rd party assembly into C# code :

C#:
# sqlitex64 https://www.nuget.org/packages/System.Data.SQLite.x64
#
# here we have to load the assembly on powershell script otherwise will be an error
# " Could not load file or assembly 'System.Data.SQLite' or one of its dependencies. The system cannot find the file specified. "
# even with 'Set-ExecutionPolicy Unrestricted' getting the same error.
# We can omit the 'LoadFile' line and execute it on powershell direct, then executing the script, is play ok....
#
# Load the System.Data.SQLite.dll assembly
Add-Type -Path "c:\yyy\System.Data.SQLite.dll"
# or [System.Reflection.Assembly]::LoadFile("c:\yyy\System.Data.SQLite.dll")
# is working on both situations...
# but no the Add-Type -AssemblyName "c:\yyy\System.Data.SQLite.dll"




if ($env:PROCESSOR_ARCHITECTURE -eq "x86") {
    Write-Host "PowerShell is running as x86 (32-bit)."
} else {
    Write-Host "PowerShell is running as x64 (64-bit)."
}

$code = @"
using System.Data;
using System.Windows.Forms;
using System.Data.SQLite;

public class General
{
    public static string RemoveXfirstChars(string dbLocation)
    {
        var sqlite = new System.Data.SQLite.SQLiteConnection("Data Source=" + dbLocation);
        sqlite.Open();
 
        var cmd = sqlite.CreateCommand();
        cmd.CommandText = "create table test(col_ID integer primary key, name text, shape blob)";
        cmd.ExecuteNonQuery();
        cmd.Dispose();
 
        sqlite.Close();
        sqlite.Dispose();
 
        MessageBox.Show("Database created!");
        return "test";
    }
}
"@

#load the C# code TYPE - and reference to C# code TYPE
if ("General" -as [type]) {} else {

    $refs = @(
        "System.Data",
        "System.Windows.Forms",
        "c:\yyy\System.Data.SQLite.dll"
    )

    # Compile and execute C# code with the specified assemblies
    Add-Type -TypeDefinition $code -ReferencedAssemblies $refs
}

try {

    $dbDir = Get-Location;
    $dbPath = [IO.Path]::Combine($dbDir, 'dbase.db');

    if (![System.IO.File]::Exists($dbPath)) {
        Write-Warning "dbase not found, it will be created"
    }

    $fileNew = [General]::RemoveXfirstChars($dbPath)
    Write-Host "dbase successfully generated!" -ForegroundColor Green
}
catch { Write-Error "Ran into an issue: $PSItem $PSItem.ScriptStackTrace" }

the error occurring because
The problem is CRL, try to load the reference DLL by the powershell directory instead of in yyy folder. more at
(2016) - nguyentoanuit.Executing C# code in PowerShell
(2010) - source - How can I get PowerShell Added-Types to use Added Types

here is the working one :
Bash:
$otherToolPath = "c:\yyy\System.Data.SQLite.dll"

if ($env:PROCESSOR_ARCHITECTURE -eq "x86") {
    Write-Host "PowerShell is running as x86 (32-bit)."
} else {
    Write-Host "PowerShell is running as x64 (64-bit)."
}

$resolver = @'
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace Utils
{
    public static class AssemblyResolver
    {
        private static Dictionary<string, string> _assemblies;
 
        static AssemblyResolver()
        {
            var comparer = StringComparer.CurrentCultureIgnoreCase;
            _assemblies = new Dictionary<string,string>(comparer);
            AppDomain.CurrentDomain.AssemblyResolve += ResolveHandler;
        }
 
        public static void AddAssemblyLocation(string path)
        {
            // This should be made threadsafe for production use
            string name = Path.GetFileNameWithoutExtension(path);
            _assemblies.Add(name, path);
        }
 
        private static Assembly ResolveHandler(object sender,
                                               ResolveEventArgs args)
        {
            var assemblyName = new AssemblyName(args.Name);
            if (_assemblies.ContainsKey(assemblyName.Name))
            {
                return Assembly.LoadFrom(_assemblies[assemblyName.Name]);
            }
            return null;
        }
    }
}
'@

$code = @"
using System.Data;
using System.Windows.Forms;
using System.Data.SQLite;

public class General
{
    public static string RemoveXfirstChars(string dbLocation)
    {
        var sqlite = new SQLiteConnection("Data Source=" + dbLocation);
        sqlite.Open();
 
        var cmd = sqlite.CreateCommand();
        cmd.CommandText = "create table test(col_ID integer primary key, name text, shape blob)";
        cmd.ExecuteNonQuery();
        cmd.Dispose();
 
        sqlite.Close();
        sqlite.Dispose();
 
        MessageBox.Show("Database created!");
        return "test";
    }
}
"@

#load the C# code TYPE - and reference the Winforms ONLY to C# code TYPE no to PS script
if (-not ('General' -as [type])) { #https://stackoverflow.com/a/66199775
#if (-not ([System.Management.Automation.PSTypeName]'General').Type)
#if ("General" -as [type]) {} else {

    $refs = @(
        "System.Data",
        "System.Windows.Forms",
        "c:\yyy\System.Data.SQLite.dll"
    )

    # Define a resolver
    Add-Type -TypeDefinition $resolver -Language CSharpVersion3
    # add assembly, use the same line to add more 3rd party DLLs
    [Utils.AssemblyResolver]::AddAssemblyLocation($otherToolPath)
 
    # Compile and execute C# code with the specified assemblies
    Add-Type -TypeDefinition $code -ReferencedAssemblies $refs
}

try {

    $cDir = Get-Location;
    $dbPath = [IO.Path]::Combine($cDir, 'dbase.db');

    if (![System.IO.File]::Exists($dbPath)) {
        Write-Warning "dbase not found, it will be created"
    }

    $fileNew = [General]::RemoveXfirstChars($dbPath)
    Write-Host "dbase successfully generated!" -ForegroundColor Green
}
catch { Write-Error "Ran into an issue: $PSItem" }


finally maybe the first example is the best for us, the code and per Microsoft
This example adds the classes from the NJsonSchema.dll assembly to the current session.

the final script is here.
 
Top