Je m'intéresse depuis quelques temps à Powershell car je dois faire des scripts pour faire installer ou migrer des systèmes que j'ai conçus/développés. J'insiste sur le "faire installer" car la plus part du temps ce n'est pas un membre de l'équipe de développement qui déploie les applications mais plutôt un membre de l'équipe de production.
PowerShell est normalement suffisamment puissant pour pouvoir faire ces tâches. Cependant, il arrive que certaines tâches requiert du code supplémentaire. Et, étant donné notre background, pourquoi ne pas faire du C# ?!
Etape 1
$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; } } } "@
# Ajout du code C# du resolver dans la session powershell Add-Type -TypeDefinition $resolver -Language CSharpVersion3
|
Etape 2
$Source = @" using System; using System.Windows.Forms;
namespace TestApplication { public class Program { public static string Test() { MessageBox.Show("$message");
return "OK"; } } } "@
|
Etape 3 : On charge les assemblies nécessaires à l'exécution du code
[Utils.AssemblyResolver]::AddAssemblyLocation("System.Windows.Forms.dll")
|
Etape 4 : On ajoute le type correspondant à notre classe dans le session Powershell courante en spécifiant les assemblies externes nécessaires (celles qu'on a chargé dans l'étape 3)
$Assem = @( "System.Windows.Forms.dll" )
# Ajout de notre code c# et de ses références dans la session powershell Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp
|
Etape 5 : On appelle la méthode de notre code
$result = [TestApplication.Program]::Test() write-host $result -foreground "green"
|
Personnellement, cette technique m'a particulièrement été utile pour créer des scripts destinés à valider/tester un programme (appel à un web service, exécution du code d'une DLL, etc.)
# Paramètre du script Param([string]$message="toto")
# Obtenir le chemin du répertoire du script c'est toujours bon $scriptpath = $MyInvocation.MyCommand.Path $dir = Split-Path $scriptpath
# Code permettant de charger les assemblies en mémoire (voir un peu plus bas) $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; } } } "@
# Ajout du code C# du resolver dans la session powershell Add-Type -TypeDefinition $resolver -Language CSharpVersion3
# Le code source $Source = @" using System; using System.Windows.Forms;
namespace TestApplication { public class Program { public static string Test() { MessageBox.Show("$message");
return "OK"; } } } "@
# Chargement des assemblies [Utils.AssemblyResolver]::AddAssemblyLocation("System.Windows.Forms.dll")
$Assem = @( "System.Windows.Forms.dll" # On peut également charger toutes les assemblies d'un répertoire ! #ForEach ( $assembly in $Assemblies) #{ # "$dir\Assemblies\$assembly" #} )
# Ajout de notre code c# et de ses références dans la session powershell Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp
$result = [TestApplication.Program]::Test() write-host $result -foreground "green"
# On attend que l'utilisateur appuie sur une touche avant de quitter Write-Host "Press any key to continue..." -ForegroundColor Magenta $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|