Programmierung

C#-DLL für die Powershell erstellen

Ich war auf der Suche nach Möglichkeiten, wie man einfach GUI-Tests für .NET Programme schreibt bzw. erstellt. Es sollte wenig kosten und möglichst schnell erstellbar sein. Gefunden habe ich was bei Microsoft. Die Idee war, dass man zu den Powershell-Funktionen ein paar weitere hinzufügt und diese mit einem Skript aufruft. Da dies für mich sehr verlockend klang, dachte ich mir, ich probiers mal aus. Das Erstellen der DLL für Powershell war dann doch nicht ganz so einfach. Hier möchte ich meine Erfahrungen zeigen, was alles passieren kann.

Die Funktionen

Zuerst brauchen wir einmal den C#-Code für unsere neue Klasse. Wir erstellen ein neues Projekt (für eine DLL). Am Schluss hatte ich folgenden Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.ComponentModel;
using System.Configuration.Install;
using System.Runtime.InteropServices;
 
namespace PSTest1
{
    [Cmdlet(VerbsCommon.Get, "TestPSTest1")]
    public class TestPSTest1Command : Cmdlet
    {
        private string testString;
 
        [Parameter(Position = 0)]
        public string TestString
        {
            get { return testString; }
            set { testString = value; }
        }
 
        protected override void ProcessRecord()
        {
            // wird an die Pipeline gesendet
            WriteObject("XXX");
        }
    }
}

Diese Funktion wird später nur XXX als Rückgabe geben. Im Grunde wird alles, was wir mit WriteObject ausgeben, dann in der Powershell ausgegeben (soweit ich weiß). Jedenfalls it das ein Grundgerüst für jede Funktion, die wir später in der Powershell aufrufen wollen. Dies können wir später mir dem Aufruf get-TestPSTest1 tun.

SnapIn-Klasse

Das ist jedoch nicht die einzige Klasse, die wir benötigen. Wir benötigen noch eine SnapIn-Klasse:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.ComponentModel;
using System.Configuration.Install;
using System.Runtime.InteropServices;
 
namespace PSTest1
{
    [RunInstaller(true)]
    public class TestPSTest1SnapIn : PSSnapIn
    {
        public TestPSTest1SnapIn()
            : base()
        {
        }
 
        public override string Name
        {
            get { return "TestPSTest1SnapIn"; }
        }
 
        public override string Vendor
        {
            get { return "Your name or something else."; }
        }
 
        public override string VendorResource
        {
            get
            {
                return "Test Test PSSnapIn";
            }
        }
 
        public override string Description
        {
            get { return "Provides cmdlets for lightweight UI automation"; }
        }
    }
}

Verweise hinzufügen

Damit ist noch nicht alles getan. Damit wir überhaupt den Namespace using System.Management.Automation; nutzen können, brauchen wir noch die richtige DLL dafür. Diese wird mit dem Powershell SDK installiert. Zu finden ist die Datei später in dem Ordner: %ProgramFiles%\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0. Es ist dann die Datei: System.Management.Automation.dll.

Wenn wir soweit sind, können wir die DLL kompilieren. Wichtig ist, dass die DLL diesselbe Zielplatform (x86, x64) hat, wie das Betriebssystem, auf dem es verwendet wird. Es kann sonst zu Fehlern führen.

Die DLL registrieren

Um die Funktionen in Powershell nutzen zu können, müssen wir die Funktion registrieren. Dazu können wir das Powershell-Skript von der Microsoft-Seite verwenden (in abgeänderter Form):

function RegisterUILib
{
write-host "registering custom cmdlets for UI automation"
$env:path = $env:path += ";C:\Windows\Microsoft.NET\Framework\v2.0.50727"
sl 'C:\Users\Unkown\Documents\Visual Studio 2010\Projects\PSTest1\PSTest1\bin\x86\Release'
installutil.exe PSTest1.dll
add-pssnapin TestPSTest1SnapIn
write-host ""
write-host "get-TestPSTest1 cmdlet is enabled"
}
 
RegisterUILib # invoke function
 
set-location C:\UIautomationWithPowerShell

Diese Skript als Powershell-Skript speichern (*.ps1) und nun können wir dieses starten. Bei mir gab es jedoch immer zwei Probleme:

  1. Installutil wollte nicht fehlerfrei durchlaufen
  2. add-pssnapin wollte nicht fehlerfrei durchlaufen

Problem mit Installutil gelöst

An die genaue Fehlerbeschreibung kann ich mich leider nicht mehr erinnern. Diese Problem hatte ich sehr schnell gelöst. Man sollte die Powershell als Administrator öffnen und dann ging da alles.

Problem mit add-pssnapin gelöst

An dem hatte ich länger zu kämpfen. Es kam regelmäßig die Fehlermeldung:

Add-PSSnapin : Cannot load Windows PowerShell snap-in VeeamPSSnapIn because
of the following error: Could not load file or assembly 
'file:///C:\Users\Unkown\Documents\Visual Studio 2010\Projects\PSTest1\PSTest1\bin\x86\Release\PSTest1.dll' 
or one of its dependencies. This assembly is built by 
a runtime newer than the currently loaded runtime and cannot be loaded. [...]

Die Lösung war hier recht einfach: Man erstell eine Powershell.Exe.Config Datei in dem Ordner %windir%\System32\WindowsPowerShell\v1.0 (auf einem 32-bit-Betriebssystem) mit folgendem Inhalt:

<?xml version="1.0"?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0.30319"/>
        <supportedRuntime version="v2.0.50727"/>
    </startup>
</configuration>

Danach sollte alles laufen. Man ruft das Skript auf (das Powershell-Skript von Microsoft) und kann dann in die Powershell eingeben: get-testpstest1.
Als Ausgabe sollten drei X’e kommen.

Hoffe ihr konntet was daraus lernen und es war halbwegs verständlich. Falls ihr Fragen, Anregungen oder ähnliches habt, könnt ihr gerne die Kommentarfunktion benutzen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.