Dajbych.net


Dvouvrstvá aplikace s PowerShellem uprostřed

, 5 minut čtení

powershell logo

Existuje několik důvodů, proč používat PowerShell. Nejvýznamnější výhodou je možnost používat skripty místo uživatelského rozhraní. Pro správce je PowerShell téměř stejné prostředí jako konzolová aplikace pro vývojáře. Pokud je jádro vaší aplikace vysoce nezávislé, je velmi snadné na jeho základě vytvořit konzolovou aplikaci, proto zvažte použití vrstvy PowerShellu.

Proč bych měl ve své aplikaci používat rozhraní PowerShell místo přímého odkazování na knihovnu? Když chce vývojář prozkoumat knihovnu, prvním krokem je obvykle konzolová aplikace. Jedná se o vývojářské pískoviště s rychlým grafickým výstupem, protože každý objekt má metodu ToString. Ne každý ví, jak napsat konzolovou aplikaci. Správci mají rádi PowerShell, protože smyčka zápis-kompilace-spuštění je mnohem rychlejší. Vývojáři mají rádi kód, ve kterém lze u každého příkazu nastavit zarážku. Takže znovu, proč rozhraní PowerShell? Odpověď není překvapivá – protože je velmi užitečná pro konkrétní druh aplikace.

Když kód něco konfiguruje, je jasné, že implementace podpory PowerShellu je skvělá investice. V mém případě jsem použil rozhraní PowerShell pro kryptoanalytickou knihovnu. Existuje mnoho algoritmů, ale nechtěl jsem napevno zakódovat kroky k dešifrování zašifrované zprávy. Chtěl jsem dát uživateli možnost používat algoritmy a rozhodovat se na základě heuristiky a vlastního úsudku, které z nich sestaví. Jedná se o podobný přístup jako pracovní postupy definované v jazyce XAML. PowerShell umí nejlépe, je rychlá interakce s uživatelem a IntelliSense se samozřejmě neztratí, protože PowerShell má schopnost automatického dokončování.

Architektura je jednoduchá. Knihovna implementuje rozhraní PowerShell. Uživatelské rozhraní spouští hostitele PowerShellu, generuje do něj příkazy a přijímá z něj objekty pro vyplnění modelů ViewModel. Implementace tohoto modelu není z mnoha důvodů triviální. V první řadě existují v zásadě dvě verze .NET – 2.0 a 4.0 (1.0 je zastaralá a 3.0 a 3.5 jsou rozšíření 2.0). PowerShell má dvě verze, přičemž třetí je v současné době v beta verzi. Další komplikací je duální prostředí – 32-bitové a 64-bitové. Strávil jsem mnoho hodin řešením problémů způsobených těmito aspekty. Když jsem konečně skončila, cítila jsem, že je mou povinností o tom napsat článek.

Promluvme si nejprve o pohledu knihovny. Knihovna musí odkazovat na knihovnu System.Management.Automation, která je součástí sady PowerShell SDK. Existují dvě důležité třídy – PSCmdlet a CustomPSSnapIn. PSCmdlet je příkaz, který může PowerShell spustit a volitelně vrátit objekt nebo kolekci. CustomPSSnapIn je sada příkazů PowerShell s informacemi o dodavateli v jednom balíčku. Vaše třídy musí být odvozeny od nich. Třídy rutin musí být ozdobeny atributem Cmdlet a třída SnapIn musí být ozdobena atributem RunInstaller. To je nutné pro reflexi, protože PowerShell tak najde rutiny ve vaší knihovně. Knihovna je zkompilována do jazyka MSIL, takže když zachováte konfiguraci Any CPU, funguje v 32bitovém i 64bitovém prostředí. Měli byste zvolit .NET Framework 3.5 pro PowerShell 2 a .NET Framework 4 pro PowerShell 3. Sestavení SnapIn zkompilované jako .NET 3.5 by mělo fungovat dobře v PowerShellu 3, ale ne naopak.

Knihovna obsahující rutiny musí být zaregistrována. Existuje InstallUtil.exe, který tuto práci udělá za vás. Problém je v tom, že tento nástroj je závislý na platformě. Vyhnout se odhadu, kterou verzi rozhraní spustit nebo zda použít 32bitovou nebo 64bitovou variantu, vyžaduje vědět, že tento nástroj je pouze obalem třídy AssemblyInstaller. To znamená, že během spouštění aplikace lze knihovnu snadno zaregistrovat, aby hostitelský PowerShell věděl o její existenci. Během ukončení aplikace je knihovna odregistrována, takže její umístění může být v budoucnu změněno, aniž by došlo k chybám. Tento přístup k registraci nevyhovuje všem scénářům. Registraci knihovny by měl provádět spíše instalační program než samotná aplikace, ale kdo má rád instalační programy? Uživatelé, kteří používají PowerShell, mají často vypnutou hodnotu User Account Protection, protože vědí, co dělají.

/* LIBRARY IMPLEMENTING POWERSHELL CMDLETS SAMPLE */

[Cmdlet(VerbsCommunications.Send, "MyCmdlet")]
public class GetMyCmdletCommand : PSCmdlet {

    [Parameter(Mandatory = true, HelpMessage = "This is a sample parameter.")]
    public string Param { get; set; }

    protected override void ProcessRecord() {
        var obj = new ObjectToReturn(Param);
        WriteObject(obj);
    }
}

[RunInstaller(true)]
public class Cryptanalysis : CustomPSSnapIn {
        
    public Cryptanalysis() {
        cmdlets = new Collection<CmdletConfigurationEntry>();
        cmdlets.Add(new CmdletConfigurationEntry("Get-MyCmdlet", typeof(GetMyCmdletCommand), null));
    }

    public override string Description {
        get { return "Demo"; }
    }

    public override string Name {
        get { return "SnapInName"; }
    }

    public override string Vendor {
        get { return "Václav Dajbych"; }
    }

    private Collection<CmdletConfigurationEntry> cmdlets;
    public override Collection<CmdletConfigurationEntry> Cmdlets {
        get {
            return cmdlets;
        }
    }
}

Z hlediska uživatelského rozhraní je to složitější. V první řadě stojí za to vědět, že i 64bitová aplikace spouští 32bitovou instanci PowerShellu. Verze rozhraní .NET Framework aplikace není důležitá. Důležitá je schopnost odkazovat na knihovnu System.Management.Automation. Tato knihovna se nachází v adresáři v1.0, ale neznamená to, že se používá PowerShell 1. Vždy se použije nejnovější dostupná verze.

Když uživatelské rozhraní spustí hostitele PowerShellu, musí nejprve načíst SnapIn pomocí třídy AddPSSnapInRunspaceConfiguration. Poté může aplikace volat metodu PowerShell.Create a používat PowerShell příkazy definované v knihovně. Výhodou PowerShellu 3 je, že je postaven na *Dynamic Language Runtime. To znamená, že můžete zadat PSObject jako dynamický a zkrátit kód.

/* USER INTERFACE INTERACTION SAMPLE */
  
// PowerShell host
Runspace runSpace;
  
private Init() {
  
  // load PowerShell
  var rsConfig = RunspaceConfiguration.Create();
  runSpace = RunspaceFactory.CreateRunspace(rsConfig);
  runSpace.Open();

  // register snapin
  using (var ps = PowerShell.Create()) {
      ps.Runspace = runSpace;
      ps.AddCommand("Get-PSSnapin");
      ps.AddParameter("Registered");
      ps.AddParameter("Name", "SnapInName");
      var result = ps.Invoke();
      if (result.Count == 0) Register(false);
  }

  // load snapin
  PSSnapInException ex;
  runSpace.RunspaceConfiguration.AddPSSnapIn("SnapInName", out ex);
}

void Register(bool undo) {
    var core = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "MySnapInLib.dll");
    using (var install = new AssemblyInstaller(core, null)) {
      IDictionary state = new Hashtable(); 
      install.UseNewContext = true;
      try {
          if (undo) {
              install.Uninstall(state);
          } else {
              install.Install(state);
              install.Commit(state);
          }
      } catch {
          install.Rollback(state);
      }
  }
}

dynamic DoJob(string parameter) {
    using (var ps = PowerShell.Create()) {
        ps.Runspace = runSpace;
        ps.AddCommand("Get-MyCmdlet");
        ps.AddParameter("Param", parameter);
        dynamic result = ps.Invoke().Single();
        return result.ReturnedObjectProperty;
    }
}

Doufám, že to pomůže při vytváření přátelských nakonfigurovaných prostředí. Jakmile budete vědět, jak na to, zjistíte, že je to opravdu snadné. Prostě dodržujte konvence pojmenování.