¿Cómo cargar ensamblajes en PowerShell?


153

El siguiente código de PowerShell

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

Da el siguiente mensaje de error:

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

Cada respuesta en Internet escribe que tengo que cargar el ensamblaje, seguro que puedo leerlo en el mensaje de error :-), la pregunta es:

¿Cómo carga el ensamblaje y hace que el script funcione?

Respuestas:


179

LoadWithPartialNameha quedado en desuso La solución recomendada para PowerShell V3 es usar el Add-Typecmdlet, por ejemplo:

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

Hay varias versiones diferentes y es posible que desee elegir una versión en particular. :-)


1
De acuerdo, uso PowerShell3: estos comandos de inclusión parecen muy complicados. Solo esperaría algo como "incluir nombre de archivo".
Baxter

66
PowerShell no distingue entre mayúsculas y minúsculas (a menos que le indique que distinga entre mayúsculas y minúsculas con operadores como -cmatch, -ceq). Por lo tanto, la carcasa de los nombres y parámetros de los comandos no importa.
Keith Hill

55
Si. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx Vea la nota en la parte superior - This API is now obsolete. Por supuesto, eso no impide que la gente lo use.
Keith Hill

2
Si bien es técnicamente correcto que LoadWithPartialNameha quedado en desuso, las razones (como se describe en blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx ) claramente no se aplican a una sesión interactiva de Powershell. Le sugiero que agregue una nota de que la API está bien para el uso interactivo de Powershell.
Micha Wiedenmann

La mayoría de las veces, no tengo ningún problema con el ensamblaje SMO, pero a veces necesito matar a PowerShell y cuando lo hago, empiezo a tener problemas de carga SMO. Agregar add-type -Path corrige eso.
Nicolas de Fontenay

73
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

8
¡Esto es demasiado útil para ser desaprobado sin un reemplazo! Mi equipo utiliza una combinación de herramientas de cliente de 2008 y 2012. Esta es la única forma de hacer que mis scripts de PowerShell funcionen para todo mi equipo sin incluir una lógica de respaldo de versión torpe.
Iain Samuel McLean Élder

44
Puede canalizar la salida a Out-Nullsi no desea que el GAC haga eco de cosas.
Iain Samuel McLean Élder

3
@Baxter: debe aceptar esta respuesta o la de Keith y dejar que esta pregunta se marque como contestada.
Jaykul

3
Uso [void] [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen

@IainElder "torpe lógica de respaldo de versión" ¡Dices eso hasta que te encuentras con incompatibilidad de versión! No es tan difícil de decir Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
Bacon Bits

44

La mayoría de las personas ya saben que System.Reflection.Assembly.LoadWithPartialNameestá en desuso, pero resulta que Add-Type -AssemblyName Microsoft.VisualBasic no se comporta mucho mejor queLoadWithPartialName :

En lugar de intentar analizar su solicitud en el contexto de su sistema, [Add-Type] mira una tabla interna estática para traducir el "nombre parcial" a un "nombre completo".

Si su "nombre parcial" no aparece en su tabla, su script fallará.

Si tiene varias versiones del ensamblaje instaladas en su computadora, no hay un algoritmo inteligente para elegir entre ellas. Obtendrá el que aparezca en su tabla, probablemente el más antiguo y anticuado.

Si las versiones que ha instalado son todas más nuevas que las obsoletas en la tabla, su script fallará.

Add-Type no tiene un analizador inteligente de "nombres parciales" como .LoadWithPartialNames.

Lo que Microsoft dice que está en realidad supone que debe hacer es algo como esto:

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

O, si conoce el camino, algo como esto:

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

Ese nombre largo dado para el ensamblado se conoce como el nombre seguro , que es exclusivo de la versión y el ensamblado, y también a veces se conoce como el nombre completo.

Pero esto deja un par de preguntas sin respuesta:

  1. ¿Cómo determino el nombre seguro de lo que realmente se está cargando en mi sistema con un nombre parcial dado?

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

Estos también deberían funcionar:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Si quiero que mi script siempre use una versión específica de un .dll pero no puedo estar seguro de dónde está instalado, ¿cómo puedo determinar cuál es el nombre seguro del .dll?

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

O:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Si conozco el nombre seguro, ¿cómo determino la ruta .dll?

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

  2. Y, en una línea similar, si sé el nombre del tipo de lo que estoy usando, ¿cómo sé de qué conjunto proviene?

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

  3. ¿Cómo veo qué conjuntos están disponibles?

Sugiero el módulo GAC PowerShell . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNamefunciona bastante bien

  1. ¿Cómo puedo ver la lista que Add-Typeusa?

Esto es un poco más complejo. Puedo describir cómo acceder a él para cualquier versión de PowerShell con un reflector .Net (consulte la actualización a continuación para PowerShell Core 6.0).

Primero, descubra de qué biblioteca Add-Typeproviene:

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

Abra la DLL resultante con su reflector. He usado ILSpy para esto porque es FLOSS, pero cualquier reflector C # debería funcionar. Abra esa biblioteca y mire adentro Microsoft.Powershell.Commands.Utility. Debajo Microsoft.Powershell.Commands, debería haber AddTypeCommand.

En el listado de código para eso, hay una clase privada, InitializeStrongNameDictionary(). Eso enumera el diccionario que asigna los nombres cortos a los nombres fuertes. Hay casi 750 entradas en la biblioteca que he visto.

Actualización: ahora que PowerShell Core 6.0 es de código abierto. Para esa versión, puede omitir los pasos anteriores y ver el código directamente en línea en su repositorio de GitHub . Sin embargo, no puedo garantizar que ese código coincida con cualquier otra versión de PowerShell.


Tercera pregunta sin respuesta: ¿qué sucede si no quiero requerir una versión específica?
jpmc26

1
@ jpmc26 Bueno, puedes usar Add-Typeo LoadWithPartialName(), pero debes tener en cuenta que el primero no será 100% consistente en todas las versiones y el último es un método obsoleto. En otras palabras, .Net quiere que le importe la versión de la biblioteca que carga.
Bacon Bits

@BaconBits La respuesta completa a la pregunta de jpmc26 es que, dependiendo de si está en PowerShell 5 o PowerShell 6, el ensamblado cargado puede ser diferente. JSON.NET tiene este problema con las funciones de Azure PS.
John Zabroski el

@BaconBits Esta es una inmersión profunda realmente fantástica en PowerShell. Tu deberias escribir un libro.
John Zabroski el

1
@KolobCanyon Porque, en ese caso, generalmente debería usar Add-Type -Path, que es el segundo código mencionado o Assembly.LoadFrom()que resuelve las dependencias para usted (y, hasta donde puedo decir, es lo que Add-Type -Pathusa). El único momento que debería usar Assembly.LoadFile()es si necesita cargar varios ensamblajes que tienen la misma identidad pero diferentes rutas. Esa es una situación extraña.
Bacon Bits

23

Si desea cargar un ensamblaje sin bloquearlo durante la sesión de PowerShell , use esto:

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

¿Dónde $storageAssemblyPathestá la ruta de archivo de su ensamblado?

Esto es especialmente útil si necesita limpiar los recursos dentro de su sesión. Por ejemplo en un script de implementación.


1
👍 👍 👍 Fantástico. Porque en Visual Studio, al depurar Powershell, la sesión de PS se cuelga después de la ejecución (a través de PowerShellToolsProcessHost). Este enfoque soluciona eso. Gracias.
CJBS


10

Puede cargar todo el conjunto * .dll con

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

3

Ninguna de las respuestas me ayudó, así que estoy publicando la solución que funcionó para mí, todo lo que tuve que hacer fue importar el módulo SQLPS, me di cuenta de esto cuando accidentalmente ejecuté el comando Restore-SqlDatabase y comencé a trabajar, lo que significa que la asamblea fue referenciada en ese módulo de alguna manera.

Solo corre:

Import-module SQLPS

Nota: Gracias Jason por notar que SQLPS está en desuso

en lugar de ejecutar:

Import-Module SqlServer

o

Install-Module SqlServer

2
Para cualquiera que use este enfoque, para su información que sqlpsestá en desuso a favor del módulosqlserver
Jason

2

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") trabajó para mi.


2

Podrías usar LoadWithPartialName. Sin embargo, eso está en desuso como dijeron.

De hecho, puede aceptar Add-Type, y además de las otras respuestas, si no desea especificar la ruta completa del archivo .dll, simplemente puede hacer:

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

Para mí, esto devolvió un error, porque no tengo instalado SQL Server (supongo), sin embargo, con esta misma idea pude cargar el ensamblado de formularios Windows Forms:

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

Puede encontrar el nombre de ensamblado preciso que pertenece a la clase particular en el sitio de MSDN:

Ejemplo de encontrar el nombre del ensamblado que pertenece a una clase particular


2

Asegúrese de tener las siguientes funciones instaladas en orden

  1. Tipos de CLR del sistema de Microsoft para SQL Server
  2. Objetos de administración compartida de Microsoft SQL Server
  3. Extensiones de Microsoft Windows PowerShell

También es posible que deba cargar

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"

Pasé una semana intentando cargar el ensamblaje y no vi ningún resultado de la declaración que los cargó, pero cuando intenté usarlo, recibí el error. Cuando instalé estas tres cosas, funcionó. - gracias
pparas

0

Agregue las referencias de ensamblaje en la parte superior.

#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

¿Podrías dar un ejemplo?
endo.anaconda

1
Simplemente necesita agregarlo al comienzo del script de PowerShell. Por ejemplo: Crear copia de seguridad de una base de datos: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Out-Null $ SQLServer = Read-Host -Prompt 'Nombre del servidor SQL (opcional)' IF ([string] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' Nombre de la base de datos SQL (opcional) 'IF ([string] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login '
Amrita Basu
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.