2016-11-10 1 views
0

J'écris actuellement une cmdlet C# en utilisant le SDK PowerShell 5.0.Cmdlet verbose stream

J'essaie de diriger l'erreur StandardError d'un exécutable tiers vers la sortie cmdlet lorsqu'elle est exécutée à partir de powershell en "temps réel". J'utilise actuellement la bibliothèque MedallionShell pour gérer l'exécution du processus. J'ai essayé ceci avec un formulaire de gain C# normal et j'ai utilisé Command.StandardError.PipeToAsync (Console.OpenStandardOutput()) pour obtenir la sortie à imprimer car l'exécutable l'a généré sur la console en "temps réel".

J'ai essayé de créer mon propre objet Stream qui appelle WriteVerbose mais il n'a pas semblé imprimer quoi que ce soit à l'écran powershell (je passe -Verbose à cmdlet quand je l'exécute).

Mon flux actuel ressemble à ceci:

  1. Ouvert Powershell ISE
  2. Charger mon module (dll C#)
  3. Call my cmdlet avec des paramètres
    • Command.Run
    • Command.StandardError.PipeToAsync (???)
    • Command.Wait (Au cours de cette étape, o l'entrée doit être transférée à la fenêtre powershell)
    • Vérifiez Commande.Résult.Succès.

Quelqu'un peut-il me diriger dans la bonne direction à ce sujet?

+0

Est-ce que votre propre 'Stream' appel de mise en œuvre' WriteVerbose' directement ou les maréchal à fil de pipeline? – PetSerAl

+0

Mon implémentation du flux a pris la cmdlet et l'a utilisée pour appeler WriteVerbose. Les octets que j'ai reçus de l'appel Write, j'ai converti en une chaîne en utilisant le Encoding.UTF8.GetString (octets). –

+0

Ce n'est pas ce que je demande. Vous ne pouvez pas simplement appeler 'WriteVerbose' sur un thread arbitraire. Alors, avez-vous convoqué les appels à 'WriteVerbose' vers le thread pipeline? – PetSerAl

Répondre

0

Vous ne pouvez pas simplement appeler Cmdlet les méthodes Write (comme WriteVerbose) à partir d'un thread arbitraire. Vous devez rassembler les appels à cette méthode vers le thread de pipeline. Un moyen de le faire est d'implémenter une boucle de message, qui traiterait les messages provenant d'autres threads, lorsque d'autres threads veulent invoquer quelque chose dans un thread de pipeline.

Add-Type @‘ 
    using System; 
    using System.Collections.Concurrent; 
    using System.Diagnostics; 
    using System.Management.Automation; 
    using System.Threading; 
    [Cmdlet(VerbsLifecycle.Invoke, "Process")] 
    public class InvokeProcessCmdlet : Cmdlet { 
     [Parameter(Position = 1)] 
     public string FileName { get; set; } 
     [Parameter(Position = 2)] 
     public string Arguments { get; set; } 
     protected override void EndProcessing() { 
      using(BlockingCollection<Action> messageQueue = new BlockingCollection<Action>()) { 
       using(Process process = new Process { 
        StartInfo=new ProcessStartInfo(FileName, Arguments) { 
         UseShellExecute=false, 
         RedirectStandardOutput=true, 
         RedirectStandardError=true 
        }, 
        EnableRaisingEvents=true 
       }) { 
        int numberOfCompleteRequests = 0; 
        Action complete =() => { 
         if(Interlocked.Increment(ref numberOfCompleteRequests)==3) { 
          messageQueue.CompleteAdding(); 
         } 
        }; 
        process.OutputDataReceived+=(sender, args) => { 
         if(args.Data==null) { 
          complete(); 
         } else { 
          messageQueue.Add(() => WriteObject(args.Data)); 
         } 
        }; 
        process.ErrorDataReceived+=(sender, args) => { 
         if(args.Data==null) { 
          complete(); 
         } else { 
          messageQueue.Add(() => WriteVerbose(args.Data)); 
         } 
        }; 
        process.Exited+=(sender, args) => complete(); 
        process.Start(); 
        process.BeginOutputReadLine(); 
        process.BeginErrorReadLine(); 
        foreach(Action action in messageQueue.GetConsumingEnumerable()) { 
         action(); 
        } 
       } 
      } 
     } 
    } 
’@ -PassThru | Select-Object -First 1 -ExpandProperty Assembly | Import-Module 

Et vous pouvez le tester avec quelque chose comme ceci:

Invoke-Process icacls 'C:\* /c' -Verbose 
+0

Votre explication a du sens, j'ai fait l'hypothèse que le WriteVerbose saurait comment gérer l'appel, peu importe où j'étais. Cependant, je ne comprends pas complètement votre exemple de code, principalement la variable 'numberOfCompleteRequests', l'action 'complete', et le foreach. Est-ce que foreach fonctionne indéfiniment jusqu'à ce que 'CompleteAdding' soit appelé? Quel est le but de la vérification == 3? –

+0

@AbdulKhan * Est-ce que foreach fonctionne indéfiniment jusqu'à ce que 'CompleteAdding' soit appelé? * - Oui. * l'action 'complete' * - Je ne veux pas répéter le code, mais je veux aussi avoir accès aux variables locales 'messageQueue' et' numberOfCompleteRequests', donc je définis 'complete' avec l'expression lambda pour avoir une fermeture sur les variables locales . * Quel est le but de la vérification == 3?* - Il y a 3 événements que je veux survenir avant de continuer: flux de sortie fermé, flux d'erreur fermé et sortie du processus, donc je vérifie que 'complete()' est appelé 3 fois. Vous pouvez choisir différents ensembles d'événements, bien sûr. – PetSerAl

+0

Merci beaucoup, cette explication m'a aidé à écrire une version de votre boucle de message adaptée à mon problème. –