Comment exécuter PowerShell depuis C# (CSharp) et récupérer le résultat sous forme d'objets

· Mis à jour le: · · C#, CSharp, PowerShell, Windows, .NET, Automatisation, Valorisation de l'existant

Il est fréquent, dans les applications métier ou les outils internes, de vouloir exécuter PowerShell depuis C#. Voici quelques exemples typiques :

  • Récupérer la liste des services Windows
  • Examiner les processus ou les journaux d’événements
  • Appeler un script PowerShell existant depuis une application C#
  • Exécuter des commandes PowerShell depuis un petit outil GUI destiné aux administrateurs
  • Intégrer progressivement, dans une application .NET, des ressources d’automatisation PowerShell déjà existantes

Pour une simple exécution, lancer powershell.exe ou pwsh.exe comme processus externe et lire la sortie standard sous forme de chaîne de caractères fonctionne aussi. Mais cette méthode fait perdre ce qui fait la force de PowerShell : le pipeline d’objets.

Le résultat de PowerShell n’est pas, à l’origine, du simple texte. Le résultat de Get-Process est un objet de processus, celui de Get-Service est un objet de service. Si le côté C# peut récupérer cette structure telle quelle, l’analyse de chaînes de caractères devient inutile et le traitement gagne nettement en sécurité.

Cet article présente les bases de l’exécution de PowerShell depuis C# et de la récupération du résultat sous forme de PSObject.

Le code présenté dans cet article est publié sur GitHub sous la forme d’un ensemble d’exemples complets, compilables et exécutables (une bibliothèque contenant le wrapper d’exécution et la logique de conversion, une démo console qui met en scène chaque section de l’article, ainsi que des tests unitaires vérifiant la récupération des PSObject et le traitement des erreurs).

csharp-run-powershell-receive-objects - komurasoft-blog-samples (GitHub)

1. Utiliser le PowerShell SDK plutôt que de lancer un processus externe

Il existe globalement deux façons d’appeler PowerShell depuis C#.

Méthode Caractéristiques Cas adaptés
Lancer powershell.exe / pwsh.exe via ProcessStartInfo Lecture de la sortie standard et de la sortie d’erreur sous forme de chaînes Exécution simple de traitements par lots existants, traitements qui se contentent de laisser des journaux
Utiliser System.Management.Automation.PowerShell Récupération du résultat sous forme de PSObject Traitement du résultat côté C#, outils d’administration, applications métier

Cet article traite de la seconde méthode. Avec System.Management.Automation.PowerShell, on peut construire et exécuter un pipeline PowerShell depuis du code C#. Le point important est que la valeur de retour n’est pas une chaîne de caractères : c’est fondamentalement une Collection<PSObject>.

Autrement dit, le raisonnement à adopter est le suivant :

Exécuter une commande PowerShell
  ↓
Récupérer le résultat sous forme de collection de PSObject
  ↓
Extraire les valeurs via BaseObject ou Properties
  ↓
Convertir si besoin en DTO / record / classe C#

L’important est de traiter la sortie de PowerShell comme des objets dès le départ, plutôt que de la décomposer sous forme de chaînes de caractères.

2. Environnement de référence

Cet article prend pour exemple une application console .NET 8. Le PowerShell SDK cible un .NET différent selon sa propre version, il faut donc choisir la version en fonction du framework cible du projet.

En juin 2026, on peut par exemple s’orienter comme suit :

Cible de l’application C# Exemple de PowerShell SDK à utiliser Remarques
.NET 8 Série 7.4 de Microsoft.PowerShell.SDK Facile à utiliser dans une application .NET 8
.NET 10 Série 7.6 de Microsoft.PowerShell.SDK Candidat lorsqu’on souhaite utiliser un PowerShell SDK plus récent
.NET Framework Microsoft.PowerShell.5.1.ReferenceAssemblies Pour Windows PowerShell 5.1 ; pour un nouveau développement, vérifier les exigences

Ici, pour l’exemple .NET 8, nous utilisons Microsoft.PowerShell.SDK en version 7.4.16.

dotnet new console -n PowerShellObjectSample
cd PowerShellObjectSample
dotnet add package Microsoft.PowerShell.SDK --version 7.4.16

Le fichier .csproj ressemble par exemple à ceci :

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

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

  <ItemGroup>
    <PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.16" />
  </ItemGroup>

</Project>

Il est recommandé de fixer précisément la version. Le PowerShell SDK est pratique, mais il est sensible à l’environnement d’exécution de l’application, au .NET ciblé et à la compatibilité des modules PowerShell. Pour une application métier, il est plus sûr de consigner explicitement la version validée que de se contenter d’utiliser « la dernière version qui fonctionnait sur le poste de développement ».

3. Code minimal : exécuter PowerShell et récupérer un PSObject

Commençons par récupérer, via PowerShell, le processus de l’application C# elle-même.

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Management.Automation;

int currentProcessId = Environment.ProcessId;

using PowerShell ps = PowerShell.Create();

Collection<PSObject> results = ps
    .AddCommand("Get-Process")
    .AddParameter("Id", currentProcessId)
    .Invoke();

foreach (PSObject item in results)
{
    Console.WriteLine($"PSObject type: {item.GetType().FullName}");
    Console.WriteLine($"BaseObject type: {item.BaseObject.GetType().FullName}");

    if (item.BaseObject is Process process)
    {
        Console.WriteLine($"Id: {process.Id}");
        Console.WriteLine($"Name: {process.ProcessName}");
        Console.WriteLine($"Memory: {process.WorkingSet64:N0} bytes");
    }
}

Il y a ici trois points à retenir : PowerShell.Create() crée l’objet d’exécution PowerShell ; AddCommand("Get-Process") et AddParameter("Id", currentProcessId) construisent la commande et ses paramètres ; et la valeur de retour d’Invoke() est une Collection<PSObject>.

PSObject est un enrobage (wrapper) autour des valeurs que PowerShell produit en sortie. Pour voir l’objet .NET d’origine qu’il contient, on regarde BaseObject. Dans cet exemple, le contenu du résultat de Get-Process peut être récupéré sous forme de System.Diagnostics.Process.

4. Choisir entre BaseObject et Properties

Lorsqu’on manipule le résultat de PowerShell en C#, la première hésitation porte sur ces deux écritures :

item.BaseObject
item.Properties["Name"]?.Value

Voici un repère pour choisir :

Méthode d’extraction Cas d’usage
BaseObject Lorsqu’on veut utiliser tel quel l’objet .NET d’origine renvoyé par PowerShell
Properties["..."] Lorsqu’on veut extraire une colonne construite avec Select-Object ou [pscustomobject]

Lorsqu’on exécute directement une commande comme Get-Process, BaseObject peut contenir l’objet .NET d’origine. En revanche, lorsque le côté PowerShell met en forme des colonnes avec Select-Object, le résultat revient le plus souvent sous forme d’objet personnalisé PowerShell. Dans ce cas, il est plus naturel de récupérer la valeur par nom de colonne via Properties.

5. Lire en C# un résultat issu de Select-Object

En pratique, on a rarement besoin de toutes les propriétés renvoyées par PowerShell. Pour ne transmettre au côté C# que les colonnes nécessaires, on utilise Select-Object dans le pipeline PowerShell.

using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;

using PowerShell ps = PowerShell.Create();

Collection<PSObject> rows = ps
    .AddCommand("Get-Process")
    .AddCommand("Sort-Object")
        .AddParameter("Property", "CPU")
        .AddParameter("Descending", true)
    .AddCommand("Select-Object")
        .AddParameter("First", 10)
        .AddParameter("Property", new[] { "Name", "Id", "CPU", "WorkingSet" })
    .Invoke();

foreach (PSObject row in rows)
{
    string name = Convert.ToString(row.Properties["Name"]?.Value, CultureInfo.InvariantCulture) ?? "";
    int id = Convert.ToInt32(row.Properties["Id"]?.Value, CultureInfo.InvariantCulture);
    double? cpu = row.Properties["CPU"]?.Value is null
        ? null
        : Convert.ToDouble(row.Properties["CPU"]!.Value, CultureInfo.InvariantCulture);
    long workingSet = Convert.ToInt64(row.Properties["WorkingSet"]?.Value, CultureInfo.InvariantCulture);

    Console.WriteLine($"{id}: {name}, CPU={cpu}, WorkingSet={workingSet:N0}");
}

Ce code correspond, en PowerShell, au pipeline suivant :

Get-Process |
  Sort-Object -Property CPU -Descending |
  Select-Object -First 10 -Property Name, Id, CPU, WorkingSet

Du point de vue de C#, enchaîner les appels à AddCommand construit le pipeline PowerShell :

.AddCommand("Get-Process")
.AddCommand("Sort-Object")
.AddCommand("Select-Object")

Écrit ainsi, la sortie de chaque commande est transmise à la commande suivante.

Une fois les colonnes restreintes avec Select-Object, on extrait les valeurs par nom de colonne, comme dans row.Properties["Name"]?.Value.

6. Convertir vers un record C#

Si l’on fait circuler PSObject dans toute l’application, le code en aval devient trop dépendant de PowerShell. Pour l’affichage ou le traitement métier, il est plus pratique de convertir vers un type C#.

Par exemple, on convertit les informations de processus vers le record suivant :

public sealed record ProcessSummary(
    string Name,
    int Id,
    double? Cpu,
    long WorkingSet);

Isoler le traitement de conversion comme suit rend le code plus lisible :

using System.Globalization;
using System.Management.Automation;

static ProcessSummary ToProcessSummary(PSObject row)
{
    string name = GetString(row, "Name");
    int id = GetInt32(row, "Id");
    double? cpu = GetNullableDouble(row, "CPU");
    long workingSet = GetInt64(row, "WorkingSet");

    return new ProcessSummary(name, id, cpu, workingSet);
}

static string GetString(PSObject row, string propertyName)
{
    return Convert.ToString(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture) ?? "";
}

static int GetInt32(PSObject row, string propertyName)
{
    return Convert.ToInt32(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture);
}

static long GetInt64(PSObject row, string propertyName)
{
    return Convert.ToInt64(row.Properties[propertyName]?.Value, CultureInfo.InvariantCulture);
}

static double? GetNullableDouble(PSObject row, string propertyName)
{
    object? value = row.Properties[propertyName]?.Value;
    return value is null ? null : Convert.ToDouble(value, CultureInfo.InvariantCulture);
}

Le code appelant devient alors :

List<ProcessSummary> processes = rows
    .Select(ToProcessSummary)
    .ToList();

foreach (ProcessSummary process in processes)
{
    Console.WriteLine($"{process.Id}: {process.Name}");
}

Traiter PSObject à la frontière avec PowerShell, et le convertir, à l’intérieur de l’application, en type C# ordinaire comme ProcessSummary.

Avec cette séparation en place, modifier plus tard la commande PowerShell limite le périmètre d’impact.

7. Renvoyer un PSCustomObject facilite le traitement côté C#

Lorsqu’on veut que le côté PowerShell renvoie plusieurs valeurs regroupées, [pscustomobject] est pratique.

using System.Collections.ObjectModel;
using System.Management.Automation;

string script = @"
[pscustomobject]@{
    MachineName       = [System.Environment]::MachineName
    PowerShellVersion = $PSVersionTable.PSVersion.ToString()
    CurrentDirectory  = (Get-Location).Path
}
";

using PowerShell ps = PowerShell.Create();

Collection<PSObject> rows = ps
    .AddScript(script, useLocalScope: true)
    .Invoke();

foreach (PSObject row in rows)
{
    Console.WriteLine($"MachineName: {row.Properties["MachineName"]?.Value}");
    Console.WriteLine($"PowerShell:  {row.Properties["PowerShellVersion"]?.Value}");
    Console.WriteLine($"Directory:   {row.Properties["CurrentDirectory"]?.Value}");
}

Si un script PowerShell renvoie un [pscustomobject] à la fin, le côté C# peut extraire les valeurs par nom depuis Properties. C’est nettement plus sûr que de renvoyer une chaîne complexe et de la découper côté C#.

Un exemple à éviter est une sortie de ce type :

"$MachineName,$PowerShellVersion,$CurrentDirectory"

Cette méthode semble simple à première vue, mais elle casse dès qu’une valeur contient une virgule ou un retour à la ligne.

Faire en sorte que PowerShell renvoie des objets, et les lire comme des propriétés côté C#. En adoptant cette forme, il devient facile de s’adapter par la suite si des colonnes sont ajoutées.

8. Ne jamais insérer directement une saisie utilisateur dans AddScript

Même en utilisant le PowerShell SDK, construire un script sous forme de chaîne de caractères est dangereux. Par exemple, un code comme celui-ci est à éviter :

// Exemple à éviter
string userInputPath = GetPathFromUser();
string script = $"Get-ChildItem -Path '{userInputPath}'";

using PowerShell ps = PowerShell.Create();
ps.AddScript(script).Invoke();

Écrit ainsi, la saisie utilisateur peut être interprétée comme du code PowerShell. Pour transmettre une valeur à une commande PowerShell, il faut privilégier AddCommand et AddParameter.

string userInputPath = GetPathFromUser();

using PowerShell ps = PowerShell.Create();

Collection<PSObject> files = ps
    .AddCommand("Get-ChildItem")
    .AddParameter("Path", userInputPath)
    .AddParameter("File", true)
    .Invoke();

Une valeur transmise via AddParameter est traitée comme une valeur de paramètre, et non concaténée sous forme de chaîne de code PowerShell.

En pratique, la répartition suivante est raisonnable :

Écriture Cas d’usage
AddCommand / AddParameter Lorsqu’on veut construire une commande de façon sûre depuis le côté C#
AddScript Pour exécuter un script court et fixe, ou pour charger un script existant
AddScript avec concaténation de chaînes À éviter en principe ; si utilisé malgré tout, valider et échapper les valeurs saisies avec une grande rigueur

Intégrer PowerShell dans C# donne à l’application des capacités opérationnelles puissantes. C’est pratique, mais il faut respecter une seule ligne rouge : ne jamais transformer directement une saisie utilisateur en script.

9. Format-Table est réservé à l’affichage final à l’écran ; ne pas l’utiliser avant de transmettre à C#

Lorsqu’on veut récupérer le résultat de PowerShell sous forme d’objets en C#, on n’utilise en principe pas Format-Table ni Format-List.

Par exemple, la commande PowerShell suivante est pratique pour un humain regardant l’écran :

Get-Service | Format-Table Name, Status

Mais si l’on utilise Format-Table avant de récupérer le résultat côté C#, celui-ci n’est plus constitué d’objets de service, mais d’informations de mise en forme destinées à l’affichage. Pour un traitement côté C#, il faut utiliser Select-Object.

Get-Service | Select-Object Name, Status

Écrit depuis C#, cela donne :

using PowerShell ps = PowerShell.Create();

Collection<PSObject> services = ps
    .AddCommand("Get-Service")
    .AddCommand("Select-Object")
        .AddParameter("Property", new[] { "Name", "Status" })
    .Invoke();

Le principe est simple :

Rendre la sortie lisible à l'écran uniquement → Format-Table / Format-List
Alimenter un traitement ultérieur en C#        → Select-Object / PSCustomObject

Cela vaut déjà lorsqu’on utilise PowerShell seul, mais cela devient particulièrement important lorsqu’on l’intègre avec C#.

10. Récupérer les erreurs

En PowerShell, la sortie et les erreurs sont des flux distincts. Se contenter de regarder la valeur de retour d’Invoke() peut faire passer à côté d’erreurs. La forme de base est la suivante :

using System.Management.Automation;

using PowerShell ps = PowerShell.Create();

Collection<PSObject> output = ps
    .AddCommand("Get-Item")
    .AddParameter("Path", @"C:\no-such-file.txt")
    .Invoke();

if (ps.HadErrors)
{
    foreach (ErrorRecord error in ps.Streams.Error)
    {
        Console.WriteLine($"Error: {error.Exception.Message}");
        Console.WriteLine($"Category: {error.CategoryInfo.Category}");
        Console.WriteLine($"Target: {error.TargetObject}");
    }
}

Les applets de commande PowerShell peuvent produire à la fois des erreurs qui interrompent le traitement et des erreurs qui le laissent se poursuivre. Si l’on veut les traiter comme des exceptions côté C#, une solution consiste à spécifier Stop pour ErrorAction.

using System.Management.Automation;

try
{
    using PowerShell ps = PowerShell.Create();

    Collection<PSObject> output = ps
        .AddCommand("Get-Item")
        .AddParameter("Path", @"C:\no-such-file.txt")
        .AddParameter("ErrorAction", "Stop")
        .Invoke();
}
catch (RuntimeException ex)
{
    Console.WriteLine($"PowerShell failed: {ex.Message}");
}

Le meilleur choix dépend de la nature de l’application. Pour un outil d’administration où l’on veut malgré tout obtenir une liste même en cas d’échec partiel, collecter le flux d’erreurs et l’afficher à l’écran est plus adapté ; si l’on veut au contraire arrêter tout le traitement en cas d’échec, traiter l’erreur comme une exception via ErrorAction Stop est plus clair.

11. Construire un petit wrapper d’exécution

Dans une application qui appelle PowerShell à de nombreuses reprises, réécrire la même gestion d’erreurs à chaque fois finit par encombrer le code. Un wrapper simple est alors pratique.

using System.Management.Automation;

public sealed record PowerShellRunResult(
    IReadOnlyList<PSObject> Output,
    IReadOnlyList<ErrorRecord> Errors);

public static class PowerShellRunner
{
    public static PowerShellRunResult Run(Action<PowerShell> build)
    {
        using PowerShell ps = PowerShell.Create();

        build(ps);

        List<PSObject> output;

        try
        {
            output = ps.Invoke().ToList();
        }
        catch (RuntimeException ex)
        {
            throw new InvalidOperationException($"PowerShell execution failed: {ex.Message}", ex);
        }

        return new PowerShellRunResult(
            Output: output,
            Errors: ps.Streams.Error.ToList());
    }
}

Le code appelant peut alors se concentrer uniquement sur la construction de la commande.

PowerShellRunResult result = PowerShellRunner.Run(ps => ps
    .AddCommand("Get-Service")
    .AddCommand("Where-Object")
        .AddParameter("Property", "Status")
        .AddParameter("EQ", "Running")
    .AddCommand("Select-Object")
        .AddParameter("First", 10)
        .AddParameter("Property", new[] { "Name", "DisplayName", "Status" }));

foreach (PSObject row in result.Output)
{
    Console.WriteLine($"{row.Properties["Name"]?.Value}: {row.Properties["Status"]?.Value}");
}

foreach (ErrorRecord error in result.Errors)
{
    Console.Error.WriteLine(error.Exception.Message);
}

Cela dit, construire depuis C# une condition spécifique à PowerShell comme le Where-Object de cet exemple peut devenir un peu difficile à lire. Pour des commandes et des paramètres simples, AddCommand / AddParameter conviennent, mais pour des filtres ou des agrégations complexes, il peut être plus lisible de prévoir un script PowerShell fixe. Même dans ce cas, le principe consistant à ne jamais concaténer directement une entrée externe dans la chaîne de script reste inchangé.

12. Laisser PowerShell mettre en forme les traitements complexes en objets

Lorsqu’on combine C# et PowerShell, répartir clairement les responsabilités de chaque côté facilite la conception.

Voici la répartition que nous recommandons :

Responsable Rôle
PowerShell Opérations proches de Windows et de ses modules, scripts existants, exécution de commandes d’administration
C# Interface utilisateur, validation des saisies, conversion de types, logique métier, persistance, intégration d’API

Côté PowerShell, on met en forme la sortie finale sous forme de [pscustomobject].

Get-Service |
  Where-Object Status -eq 'Running' |
  Select-Object Name, DisplayName, Status

Ou bien on construit explicitement un [pscustomobject] :

$services = Get-Service | Where-Object Status -eq 'Running'

[pscustomobject]@{
    Count = $services.Count
    Names = $services.Name
}

Côté C#, on lit les propriétés du PSObject reçu et on les convertit vers les types propres à l’application.

En adoptant cette forme, on évite que les détails d’implémentation de PowerShell ne se propagent trop largement du côté C#.

13. Points de vigilance fréquents en pratique

Lorsqu’on exécute PowerShell depuis C#, il ne suffit pas que le code fonctionne. En pratique, vérifier tôt les points suivants permet de rester en sécurité.

Les privilèges de l’utilisateur d’exécution

PowerShell s’exécute avec les privilèges de l’utilisateur qui exécute l’application C#. Les commandes nécessitant des privilèges d’administrateur échouent lorsqu’elles sont exécutées avec un utilisateur standard. Les opérations sur les services, les journaux d’événements, les certificats, le registre, Hyper-V, ou les modules d’administration Microsoft 365, nécessitent tous de bien répartir les niveaux de privilège.

Différences entre 32 bits et 64 bits

Sous Windows, le registre et les modules visibles peuvent différer entre un processus 32 bits et un processus 64 bits. Pour un outil d’administration Windows, partir du principe d’une exécution en x64 réduit en général les problèmes.

La présence des modules dans l’environnement d’exécution

Ajouter le PowerShell SDK à une application C# n’installe pas automatiquement tous les modules PowerShell. Par exemple, si l’on utilise un module d’administration propre à un produit ou un module interne à l’entreprise, il faut vérifier que ce module existe bien dans l’environnement d’exécution, et depuis quel chemin il sera chargé.

Dans les applications graphiques, ne pas bloquer le thread d’interface

Lorsqu’on exécute PowerShell depuis WinForms ou WPF, exécuter un traitement lourd directement sur le thread d’interface fige la fenêtre. Dans ce cas, il faut exécuter le traitement en arrière-plan et mettre à jour l’interface une fois terminé. Le PowerShell SDK propose aussi des API d’exécution asynchrone, mais la première règle à respecter reste de ne jamais faire un Invoke() long sur le thread d’interface.

La taille lors de la distribution de l’application

Microsoft.PowerShell.SDK est pratique, mais il augmente les dépendances embarquées dans l’application. Cela peut rester acceptable pour un petit utilitaire, mais selon le format de distribution et le mode de mise à jour, la taille peut devenir un problème. ClickOnce, MSIX, exécutable unique, outil de déploiement interne : il est rassurant de vérifier tôt avec le mode de distribution réellement utilisé.

14. L’intérêt de recevoir des objets plutôt que des chaînes de caractères

Pour finir, pourquoi s’obstiner autant sur PSObject ? Exécuter PowerShell en lançant un processus externe et en lisant la sortie standard est simple.

Sortie de PowerShell
  ↓
Chaîne de caractères
  ↓
Split / expression régulière / Substring
  ↓
Valeur C#

Mais cette méthode dépend du format d’affichage. Elle casse facilement selon la largeur des colonnes, la locale, les retours à la ligne, les espaces, les messages d’erreur, ou les caractères de séparation présents dans les valeurs.

Avec le PowerShell SDK, en revanche, le flux devient :

Sortie de PowerShell
  ↓
PSObject
  ↓
Properties / BaseObject
  ↓
Type C#

Ici, on extrait les valeurs en s’appuyant sur la structure de données plutôt que sur le format d’affichage. Pour les applications métier et les outils d’administration, cette seconde approche est plus facile à maintenir.

15. Conclusion

Si l’on veut exécuter PowerShell depuis C# et exploiter le résultat, il vaut la peine d’envisager le PowerShell SDK plutôt que de se contenter de lancer powershell.exe et de lire la sortie standard.

Le flux de base est le suivant :

Ajouter Microsoft.PowerShell.SDK
  ↓
Créer l'objet d'exécution avec PowerShell.Create()
  ↓
Construire le traitement avec AddCommand / AddParameter / AddScript
  ↓
Exécuter avec Invoke()
  ↓
Récupérer une Collection<PSObject>
  ↓
Extraire les valeurs via BaseObject ou Properties
  ↓
Convertir vers un DTO / record / classe C#

Trois points sont particulièrement importants en pratique :

  • Pour un traitement ultérieur en C#, utiliser Select-Object ou [pscustomobject], et non Format-Table
  • Ne jamais insérer directement une saisie utilisateur dans la chaîne d’AddScript ; la transmettre autant que possible via AddParameter
  • Traiter PSObject à la frontière, et le convertir en type C# à l’intérieur de l’application

PowerShell est puissant pour l’administration de Windows et la valorisation de l’existant ; C# est puissant pour la création d’applications, d’interfaces graphiques et de traitements métier sûrs du point de vue des types. En reliant intelligemment les deux, on peut faire progressivement évoluer des scripts PowerShell existants vers une application .NET, sans avoir à les jeter.

Références

  • L’ensemble du code d’exemple de cet article (bibliothèque, démo, tests unitaires) https://github.com/gomurin0428/komurasoft-blog-samples/tree/main/csharp-run-powershell-receive-objects
  • Microsoft Learn : Démarrage rapide de l’hôte Windows PowerShell
    https://learn.microsoft.com/fr-fr/powershell/scripting/developer/hosting/windows-powershell-host-quickstart
  • Microsoft Learn : Ajout et appel de commandes
    https://learn.microsoft.com/fr-fr/powershell/scripting/developer/hosting/adding-and-invoking-commands
  • Microsoft Learn : Classe PowerShell
    https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.powershell
  • Microsoft Learn : Classe PSObject
    https://learn.microsoft.com/fr-fr/dotnet/api/system.management.automation.psobject
  • NuGet Gallery : Microsoft.PowerShell.SDK
    https://www.nuget.org/packages/Microsoft.PowerShell.SDK/
  • NuGet Gallery : Microsoft.PowerShell.5.1.ReferenceAssemblies
    https://www.nuget.org/packages/Microsoft.PowerShell.5.1.ReferenceAssemblies/

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Quelle est la meilleure méthode pour exécuter PowerShell depuis C# ?
On distingue globalement deux méthodes : lancer powershell.exe/pwsh.exe comme processus externe via ProcessStartInfo, ou utiliser System.Management.Automation.PowerShell (le PowerShell SDK). Si vous vous contentez de lire la sortie standard sous forme de chaîne, la première méthode fonctionne aussi. Mais pour un outil d'administration ou une application métier où C# doit retraiter le résultat, la seconde méthode, qui permet de récupérer le résultat sous forme de collection de PSObject, est plus adaptée. Elle évite d'avoir à analyser des chaînes de caractères et permet d'écrire un traitement sûr, indépendant du format d'affichage.
Comment choisir entre BaseObject et Properties sur un PSObject ?
BaseObject s'utilise lorsqu'on veut récupérer tel quel l'objet .NET d'origine renvoyé par PowerShell. Par exemple, le résultat d'un Get-Process exécuté tel quel peut être récupéré sous forme de System.Diagnostics.Process. En revanche, un résultat dont les colonnes ont été mises en forme avec Select-Object ou [pscustomobject] revient le plus souvent sous forme d'objet personnalisé PowerShell ; dans ce cas, il est plus naturel de récupérer la valeur via Properties["NomDeColonne"]?.Value en indiquant le nom de la colonne.
À quoi faut-il faire attention lorsqu'on transmet une saisie utilisateur de C# vers PowerShell ?
Il faut éviter d'insérer une saisie utilisateur par concaténation de chaînes dans le script d'AddScript, car cela laisse la possibilité que la saisie soit interprétée comme du code PowerShell, ce qui est dangereux. Pour transmettre une valeur, utiliser AddCommand et AddParameter permet de traiter la valeur comme un paramètre et non comme une chaîne de code. Il est plus prudent de réserver AddScript aux scripts courts et fixes ou au chargement de scripts existants.
Pourquoi ne faut-il pas utiliser Format-Table lorsqu'on récupère le résultat de PowerShell en C# ?
Parce qu'en passant par Format-Table ou Format-List, le résultat n'est plus l'objet d'origine mais des informations de mise en forme destinées à l'affichage, et qu'il devient alors impossible d'en extraire les valeurs sous forme de propriétés côté C#. Si le résultat doit servir à un traitement ultérieur en C#, il faut soit restreindre les colonnes avec Select-Object, soit faire en sorte que PowerShell renvoie un [pscustomobject]. En résumé : « pour un simple affichage à l'écran, la famille Format ; pour une transmission à C#, Select-Object ».

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog