Comment ajouter correctement des assemblys .NET à une session Powershell?

24

J'ai un assemblage .NET (une dll) qui est une API pour sauvegarder le logiciel que nous utilisons ici. Il contient certaines propriétés et méthodes dont j'aimerais tirer parti dans mes scripts Powershell. Cependant, je rencontre beaucoup de problèmes avec le premier chargement de l'assemblage, puis l'utilisation de l'un des types une fois l'assemblage chargé.

Le chemin d'accès complet au fichier est:

C:\rnd\CloudBerry.Backup.API.dll

Dans Powershell, j'utilise:

$dllpath = "C:\rnd\CloudBerry.Backup.API.dll"
Add-Type -Path $dllpath

J'obtiens l'erreur ci-dessous:

Add-Type : Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information.
At line:1 char:9
+ Add-Type <<<<  -Path $dllpath
+ CategoryInfo          : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
+ FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeComma
ndAdd-Type : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.

L'utilisation de la même applet de commande sur un autre assembly .NET, DotNetZip , qui contient des exemples d'utilisation des mêmes fonctionnalités sur le site, ne fonctionne pas non plus pour moi.

Je trouve finalement que je suis apparemment capable de charger l'assemblage en utilisant la réflexion:

[System.Reflection.Assembly]::LoadFrom($dllpath)

Bien que je ne comprenne pas la différence entre les méthodes Load, LoadFrom ou LoadFile, cette dernière méthode semble fonctionner.

Cependant, il me semble toujours impossible de créer des instances ou d'utiliser des objets. Chaque fois que j'essaye, j'obtiens des erreurs qui décrivent que Powershell est incapable de trouver l'un des types publics.

Je sais que les cours sont là:

$asm = [System.Reflection.Assembly]::LoadFrom($dllpath)
$cbbtypes = $asm.GetExportedTypes()
$cbbtypes | Get-Member -Static

---- début de l'extrait ----

   TypeName: CloudBerryLab.Backup.API.BackupProvider

Name                MemberType Definition
----                ---------- ----------
PlanChanged         Event          System.EventHandler`1[CloudBerryLab.Backup.API.Utils.ChangedEventArgs] PlanChanged(Sy...
PlanRemoved         Event          System.EventHandler`1[CloudBerryLab.Backup.API.Utils.PlanRemoveEventArgs] PlanRemoved...
CalculateFolderSize Method     static long CalculateFolderSize()
Equals              Method     static bool Equals(System.Object objA, System.Object objB)
GetAccounts         Method     static CloudBerryLab.Backup.API.Account[],     CloudBerry.Backup.API, Version=1.0.0.1, Cu...
GetBackupPlans      Method     static CloudBerryLab.Backup.API.BackupPlan[], CloudBerry.Backup.API, Version=1.0.0.1,...
ReferenceEquals     Method     static bool ReferenceEquals(System.Object objA, System.Object objB)
SetProfilePath      Method     static System.Void SetProfilePath(string profilePath)

---- fin de l'extrait ----

Essayer d'utiliser des méthodes statiques échoue, je ne sais pas pourquoi !!!

[CloudBerryLab.Backup.API.BackupProvider]::GetAccounts()
Unable to find type [CloudBerryLab.Backup.API.BackupProvider]: make sure that the     assembly containing this type is load
ed.
At line:1 char:42
+ [CloudBerryLab.Backup.API.BackupProvider] <<<< ::GetAccounts()
    + CategoryInfo          : InvalidOperation:     (CloudBerryLab.Backup.API.BackupProvider:String) [], RuntimeException
    + FullyQualifiedErrorId : TypeNotFound

Toute orientation appréciée !!

amandion
la source

Réponses:

15

Pourriez-vous entourer le Add-Typeavec un try catch et imprimer la propriété LoaderExceptions, comme l'erreur indique de le faire. Il peut fournir une exception avec un message d'erreur plus détaillé.

try
{
    Add-Type -Path "C:\rnd\CloudBerry.Backup.API.dll"
}
catch
{
    $_.Exception.LoaderExceptions | %
    {
        Write-Error $_.Message
    }
}
jessenich
la source
9
Cela n'a pas fonctionné pour moi, mais cela m'a mis dans le stade. À l'intérieur de la capture, l'objet LoaderExceptions se trouve ici: $ _. Exception.LoaderExceptions
Brett
n'existe-t-il pas un moyen d'utiliser un chemin relatif?
Amit
3

J'ai trouvé ce lien: http://www.madwithpowershell.com/2013/10/add-type-vs-reflectionassembly-in.html

Il dit que ".LoadWithPartialName" est obsolète. Par conséquent, au lieu de continuer à implémenter Add-Type avec cette méthode, il utilise une table interne statique pour traduire le "nom partiel" en "nom complet". Dans l'exemple donné dans la question, CloudBerry.Backup.API.dlln'a pas d'entrée dans la table interne de PowerShell, c'est pourquoi cela [System.Reflection.Assembly]::LoadFrom($dllpath)fonctionne. Il n'utilise pas la table pour rechercher un nom partiel.

Slogmeister Extraordinaire
la source
2

Certaines des méthodes ci-dessus n'ont pas fonctionné pour moi ou n'étaient pas claires.

Voici ce que j'utilise pour encapsuler les appels -AddPath et intercepter les LoaderExceptions:

try
{
   Add-Type -Path "C:\path\to.dll"
}
catch [System.Reflection.ReflectionTypeLoadException]
{
   Write-Host "Message: $($_.Exception.Message)"
   Write-Host "StackTrace: $($_.Exception.StackTrace)"
   Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
}

Référence
https://social.technet.microsoft.com/Forums/sharepoint/en-US/dff8487f-69af-4b64-ab83-13d58a55c523/addtype-inheritance-loaderexceptions

Ralph Willgoss
la source
0

J'ai utilisé la configuration suivante pour charger un contrôle csharp personnalisé dans PowerShell. Il permet au contrôle d'être personnalisé et utilisé à partir de PowerShell.

voici le lien du blog

http://justcode.ca/wp/?p=435

et voici le lien codeproject avec la source

http://www.codeproject.com/Articles/311705/Custom-CSharp-Control-for-Powershell

justcode_ca
la source
3
Bienvenue dans Server Fault! Nous préférons vraiment que les réponses contiennent du contenu et non des pointeurs vers le contenu. Bien que cela puisse théoriquement répondre à la question, il serait préférable d'inclure ici les parties essentielles de la réponse et de fournir le lien de référence.
jscott
0

Ils LoaderExceptionssont cachés dans l'enregistrement d'erreur. Si l'erreur de type ajout était la dernière de la liste d'erreurs, utilisez $Error[0].InnerException.LoaderExceptionspour afficher les erreurs. Très probablement, votre bibliothèque dépend d'une autre qui n'a pas été chargée. Vous pouvez soit Add-Typechacun, soit créer une liste et utiliser l' -ReferencedAssembliesargument pour Add-Type.

Eris
la source
Essayez $ Error [0] .Exception.LoaderExceptions et suivez les conseils d'Eris.
Tahir Hassan
-1

Je suppose que maintenant vous POURRIEZ avez trouvé une réponse à ce phénomène. J'ai couru sur ce message après avoir rencontré le même problème ... J'ai pu charger l'assembly et afficher les types contenus dans l'assembly, mais je n'ai pas pu instancier une instance de celui-ci à partir d'une classe statique. Était-ce EFTIDY. Tidy, EFTidyNet.TidyNet.Options ou quoi? Ooooo Weeee ... Des problèmes ... des problèmes ... ça pourrait être n'importe quoi. Et regarder à travers les méthodes statiques et les types de DLL n'a rien révélé de prometteur. Maintenant, je déprimais. Je l'avais fait fonctionner dans un programme C # compilé, mais pour mon usage, je voulais le faire fonctionner dans un langage interpété ... powershell.

J'ai trouvé ma solution, et elle est toujours en cours de preuve, mais je suis ravie et je voulais la partager. Construisez une petite application console.exe exerçant la fonction qui m'intéressait, puis visualisez-la dans quelque chose qui la décompilerait ou afficherait le code IL. J'ai utilisé le réflecteur de Red-Gate et le complément du générateur de langage PowerShell et Wallah! il a montré quelle était la chaîne de construction appropriée! :-) Essayez-le. et j'espère que cela fonctionne pour quiconque est confronté à ce problème.

Kim D. Alexander
la source
2
Et ... quelle était la chaîne constructeur appropriée? Cela ne répond pas vraiment à la question telle qu'elle est écrite. Bienvenue également à ServerFault!
Austinian