Wie lade ich Assemblys in PowerShell?

153

Der folgende PowerShell-Code

#Get a server object which corresponds to the default instance
$srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
... rest of the script ...

Gibt die folgende Fehlermeldung aus:

New-Object : Cannot find type [Microsoft.SqlServer.Management.SMO.Server]: make sure 
the assembly containing this type is loaded.
At C:\Users\sortelyn\ ... \tools\sql_express_backup\backup.ps1:6  char:8
+ $srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Jede Antwort im Internet schreibt, dass ich die Assembly laden muss - sicher kann ich das aus der Fehlermeldung lesen :-) - die Frage ist:

Wie lade ich die Assembly und lasse das Skript funktionieren?

Baxter
quelle

Antworten:

179

LoadWithPartialNamewurde veraltet. Die empfohlene Lösung für PowerShell V3 ist die Verwendung des Add-TypeCmdlets, z.

Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'

Es gibt mehrere verschiedene Versionen, und Sie möchten möglicherweise eine bestimmte Version auswählen. :-)

Keith Hill
quelle
1
Okay, ich benutze PowerShell3 - diese Include-Befehle scheinen sehr kompliziert zu sein. Ich würde nur so etwas wie "Dateiname einschließen" erwarten.
Baxter
6
PowerShell unterscheidet nicht zwischen Groß- und Kleinschreibung (es sei denn, Sie geben an, dass bei Operatoren wie -cmatch, -ceq zwischen Groß- und Kleinschreibung unterschieden werden soll). Das Gehäuse für Befehlsnamen und Parameter spielt also keine Rolle.
Keith Hill
5
Ja. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx Siehe den Hinweis oben - Das This API is now obsolete. hindert die Leute natürlich nicht daran, ihn zu verwenden.
Keith Hill
2
Obwohl es technisch korrekt ist, dass LoadWithPartialNamees veraltet ist, gelten die Gründe (wie in blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx beschrieben ) eindeutig nicht für eine interaktive Powershell-Sitzung. Ich schlage vor, Sie fügen einen Hinweis hinzu, dass die API für die interaktive Verwendung von Powershell in Ordnung ist.
Micha Wiedenmann
Die meiste Zeit habe ich kein Problem mit der SMO-Assembly, aber manchmal muss ich Powershell beenden, und wenn ich dies tue, treten Probleme beim Laden von SMO auf. Das Hinzufügen von add-type -Path behebt das.
Nicolas de Fontenay
73
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
Shay Levy
quelle
8
Dies ist zu nützlich, um ersatzlos veraltet zu sein! Mein Team verwendet eine Mischung aus Client-Tools für 2008 und 2012. Dies ist die einzige Möglichkeit, meine PowerShell-Skripte für mein gesamtes Team zum Laufen zu bringen, ohne umständliche Versions-Fallback-Logik einzubeziehen.
Iain Samuel McLean Elder
4
Sie können die Ausgabe an weiterleiten, Out-Nullwenn Sie nicht möchten, dass der GAC Daten wiedergibt.
Iain Samuel McLean Elder
3
@Baxter - Sie sollten diese Antwort oder die von Keith akzeptieren und diese Frage als beantwortet markieren.
Jaykul
3
Ich benutze [void] [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen
@IainElder "ungeschickte Versions-Fallback-Logik" Das sagst du, bis du auf Versionsinkompatibilität stößt! Es ist nicht so schwer zu sagen Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
Bacon Bits
44

Die meisten Menschen wissen inzwischen, dass dies System.Reflection.Assembly.LoadWithPartialNameveraltet ist, aber es stellt sich heraus, dass Add-Type -AssemblyName Microsoft.VisualBasic es sich nicht viel besser verhält alsLoadWithPartialName :

Anstatt zu versuchen, Ihre Anfrage im Kontext Ihres Systems zu analysieren, betrachtet [Add-Type] eine statische interne Tabelle, um den "Teilnamen" in einen "vollständigen Namen" zu übersetzen.

Wenn Ihr "Teilname" nicht in der Tabelle angezeigt wird, schlägt Ihr Skript fehl.

Wenn auf Ihrem Computer mehrere Versionen der Baugruppe installiert sind, können Sie keinen intelligenten Algorithmus auswählen. Sie werden das bekommen, was in ihrer Tabelle erscheint, wahrscheinlich das ältere, veraltete.

Wenn die von Ihnen installierten Versionen alle neuer als die veraltete in der Tabelle sind, schlägt Ihr Skript fehl.

Add-Type hat keinen intelligenten Parser von "Teilnamen" wie .LoadWithPartialNames.

Was Microsoft sagt, dass Sie eigentlich tun sollen, ist ungefähr so:

Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

Oder, wenn Sie den Weg kennen, so etwas:

Add-Type -Path 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualBasic\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualBasic.dll'

Dieser lange Name für die Versammlung ist bekannt als starker Name bezeichnet , der sowohl für die Version als auch für die Assembly eindeutig ist und manchmal auch als vollständiger Name bezeichnet wird.

Dies lässt jedoch einige Fragen offen:

  1. Wie bestimme ich den starken Namen dessen, was tatsächlich mit einem bestimmten Teilnamen auf mein System geladen wird?

    [System.Reflection.Assembly]::LoadWithPartialName($TypeName).Location; [System.Reflection.Assembly]::LoadWithPartialName($TypeName).FullName;

Diese sollten auch funktionieren:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Wenn ich möchte, dass mein Skript immer eine bestimmte Version einer DLL verwendet, ich aber nicht sicher bin, wo sie installiert ist, wie kann ich den starken Namen der DLL ermitteln?

    [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;

Oder:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Wie bestimme ich den DLL-Pfad, wenn ich den starken Namen kenne?

    [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;

  2. Und in ähnlicher Weise, wenn ich den Typnamen meiner Verwendung kenne, woher weiß ich, von welcher Baugruppe sie stammt?

    [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName

  3. Wie sehe ich, welche Baugruppen verfügbar sind?

Ich schlage das GAC PowerShell-Modul vor . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNamefunktioniert ziemlich gut.

  1. Wie kann ich die verwendete Liste Add-Typeanzeigen?

Das ist etwas komplexer. Ich kann beschreiben, wie für jede Version von PowerShell mit einem .Net-Reflektor darauf zugegriffen werden kann (siehe das Update unten für PowerShell Core 6.0).

Stellen Sie zunächst fest, aus welcher Bibliothek Sie Add-Typestammen:

Get-Command -Name Add-Type | Select-Object -Property DLL

Öffnen Sie die resultierende DLL mit Ihrem Reflektor. Ich habe ILSpy dafür verwendet, weil es FLOSS ist, aber jeder C # -Reflektor sollte funktionieren. Öffnen Sie diese Bibliothek und schauen Sie hinein Microsoft.Powershell.Commands.Utility. Unter Microsoft.Powershell.Commandssollte es seinAddTypeCommand .

In der Codeliste dafür gibt es eine private Klasse InitializeStrongNameDictionary(). Das listet das Wörterbuch auf, das die Kurznamen den starken Namen zuordnet. Es gibt fast 750 Einträge in der Bibliothek, die ich mir angesehen habe.

Update: Jetzt, da PowerShell Core 6.0 Open Source ist. Für diese Version können Sie die obigen Schritte überspringen und den Code direkt online in ihrem GitHub-Repository anzeigen . Ich kann jedoch nicht garantieren, dass dieser Code mit einer anderen Version von PowerShell übereinstimmt.

Speckwürfel
quelle
3. unbeantwortete Frage: Was ist, wenn ich keine bestimmte Version benötigen möchte?
jpmc26
1
@ jpmc26 Nun, Sie können einfach Add-Typeoder verwenden LoadWithPartialName(), aber Sie müssen sich bewusst sein, dass Ersteres nicht über Versionen hinweg 100% konsistent sein wird und Letzteres eine veraltete Methode ist. Mit anderen Worten, .Net möchte, dass Sie sich um die Version der Bibliothek kümmern, die Sie laden.
Bacon Bits
@BaconBits Die vollständige Antwort auf die Frage von jpmc26 lautet, dass die geladene Assembly je nachdem, ob Sie PowerShell 5 oder PowerShell 6 verwenden, unterschiedlich sein kann. JSON.NET hat dieses Problem mit Azure PS-Funktionen.
John Zabroski
@ BaconBits Dies ist ein wirklich fantastischer tiefer Einblick in PowerShell. Du solltest ein Buch schreiben.
John Zabroski
1
@KolobCanyon Weil Sie in diesem Fall im Allgemeinen Add-Type -Pathden zweiten genannten Code verwenden sollten oder Assembly.LoadFrom()der Abhängigkeiten für Sie auflöst (und, soweit ich das beurteilen kann, welche Add-Type -Pathverwendet werden). Sie sollten nur dann verwenden, Assembly.LoadFile()wenn Sie mehrere Assemblys laden müssen, die dieselbe Identität, aber unterschiedliche Pfade haben. Das ist eine seltsame Situation.
Bacon Bits
23

Wenn Sie eine Assembly während der Dauer der PowerShell-Sitzung laden möchten, ohne sie zu sperren, verwenden Sie Folgendes :

$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)

Wo $storageAssemblyPath ist der Dateipfad Ihrer Assembly?

Dies ist besonders nützlich, wenn Sie die Ressourcen in Ihrer Sitzung bereinigen müssen. Zum Beispiel in einem Bereitstellungsskript.

Martin Brandl
quelle
1
👍 👍 👍 Fantastisch. Denn in Visual Studio bleibt die PS-Sitzung beim Debuggen von Powershell nach der Ausführung hängen (über PowerShellToolsProcessHost). Dieser Ansatz behebt das. Vielen Dank.
CJBS
10

Sie können die gesamte * .dll-Assembly mit laden

$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");
Yanaki
quelle
3

Keine der Antworten hat mir geholfen, daher veröffentliche ich die für mich funktionierende Lösung. Ich musste lediglich das SQLPS-Modul importieren. Dies wurde mir klar, als ich versehentlich den Befehl Restore-SqlDatabase ausführte und anfing zu arbeiten Die Assembly wurde in diesem Modul irgendwie referenziert.

Renn einfach:

Import-module SQLPS

Hinweis: Vielen Dank an Jason, dass er festgestellt hat, dass SQLPS veraltet ist

Führen Sie stattdessen Folgendes aus:

Import-Module SqlServer

oder

Install-Module SqlServer
dim_user
quelle
2
Für jeden, der diesen Ansatz verwendet, FYI, sqlpsdas zugunsten des Moduls veraltet istsqlserver
Jason
2

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") arbeitete für mich.

Tez Kurmala
quelle
2

Sie könnten verwenden LoadWithPartialName. Dies ist jedoch wie gesagt veraltet.

Sie können in der Tat Add-Typemitmachen und zusätzlich zu den anderen Antworten, wenn Sie nicht den vollständigen Pfad der DLL-Datei angeben möchten, können Sie einfach Folgendes tun:

Add-Type -AssemblyName "Microsoft.SqlServer.Management.SMO"

Für mich gab dies einen Fehler zurück, da ich SQL Server nicht installiert habe (ich denke), aber mit der gleichen Idee konnte ich die Windows Forms-Assembly laden:

Add-Type -AssemblyName "System.Windows.Forms"

Den genauen Assemblernamen der jeweiligen Klasse finden Sie auf der MSDN-Site:

Beispiel für die Ermittlung des Assemblynamens einer bestimmten Klasse

ThomasMX
quelle
2

Stellen Sie sicher, dass die folgenden Funktionen der Reihe nach installiert sind

  1. Microsoft System CLR-Typen für SQL Server
  2. Gemeinsame Microsoft SQL Server-Verwaltungsobjekte
  3. Microsoft Windows PowerShell-Erweiterungen

Möglicherweise müssen Sie auch laden

Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll"
Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.SqlWmiManagement.dll"
Shadi Eftekhari
quelle
Ich habe eine Woche lang versucht, die Assembly zu laden, und keine Ausgabe der Anweisung gesehen, die sie geladen hat, aber als ich versuchte, sie zu verwenden, wurde der Fehler angezeigt. Als ich diese drei Dinge installiert habe, hat es funktioniert. - danke
pparas
0

Fügen Sie die Baugruppenreferenzen oben hinzu.

#Load the required assemblies SMO and SmoExtended.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
Amrita Basu
quelle
Könntest du ein Beispiel daraus machen?
Endo.anaconda
1
Sie müssen es lediglich am Anfang des Powershell-Skripts hinzufügen. Zum Beispiel: Backup einer Datenbank erstellen: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Out-Null $ SQLServer = Read-Host -Prompt 'SQL Server-Name (optional)' IF ([Zeichenfolge] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' SQL-Datenbankname (optional) 'IF ([Zeichenfolge] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login '
Amrita Basu