Types .NET dans les classes PowerShell
J'essaie de comprendre les meilleures pratiques de PowerShell Class et je me heurte à une certaine confusion avec une classe simple pour gérer les info-bulles.
Add-Type -assemblyName:System.Drawing
Add-Type -assemblyName:System.Windows.Forms
class PxMessage {
static [PxMessage] $instance static $balloon
static $defaultIcon static [PxMessage] GetInstance($processIcon) {
if ([PxMessage]::instance -eq $null) { [PxMessage]::instance = [PxMessage]::new() #[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new() [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon [PxMessage]::defaultIcon = $processIcon
}
return [PxMessage]::instance
}
[Void] SendMessage ([String]$title, [String]$message, [String]$messageIcon) { [PxMessage]::balloon.icon = [PxMessage]::defaultIcon [PxMessage]::balloon.balloonTipTitle = $title
[PxMessage]::balloon.balloonTipText = $message [PxMessage]::balloon.balloonTipIcon = $messageIcon
[PxMessage]::balloon.visible = $true [PxMessage]::balloon.ShowBalloonTip(0) [PxMessage]::balloon.Dispose } } $processIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($(Get-Process -id:$PID | Select-Object -expandProperty:path))
$message = [PxMessage]::GetInstance($processIcon)
$message.SendMessage('Title', "$(Get-Date)", 'Info')
J'ai deux questions:
1: Pourquoi [PxMessage]::balloon = New-Object Windows.Forms.NotifyIconfonctionne, mais [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()ne fonctionne pas ( Unable to find typeerreur)? Et cela suggère-t-il que l'utilisation [Type]::new()n'est pas encore entièrement prise en charge, et par souci de cohérence, je ferais mieux d'utiliser New-Object partout? Ou du moins partout dans mes propres cours?
2: Je voudrais taper mes propriétés & paramètres, mais j'obtiens également des Unable to find typeerreurs lorsque je tape les $balloon& $defaultIconpropriétés, ou si je tape le $processIconparamètre dans la GetInstanceméthode.
Évidemment, je peux taper Propriétés, même si mon type est défini. Alors, qu'est-ce qui est différent entre les deux [System.Drawing]& [System.Windows.Forms], et est-ce un bogue ou une fonctionnalité? Et y a-t-il d'autres types qui se comportent de la même manière?
Réponses
C'est essentiellement une condition de course!
Lorsque PowerShell commence à exécuter un fichier de script, il passe par 3 phases:
- Analyse
- Compilation
- Exécution
La toute première chose qui est traitée dans la phase de compilation (donc avant même que l'exécution ne commence) est:
- Toutes les
usinginstructions en haut du script - Toutes les définitions de type - any
classouenummots-clés - sont compilées séparément
Ainsi, les erreurs liées à la résolution du [Windows.Forms.NotifyIcon]littéral de type dans la définition de classe sont en fait lancées avant d' Add-Type -AssemblyName:System.Windows.Forms avoir une chance de s'exécuter!
Deux options:
Scripts imbriqués
Écrivez un script de chargeur distinct qui charge la dépendance:
# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
[Windows.Forms.NotifyIcon]$BalloonTipIcon
}
Avec modules
Avec les modules, il est un peu plus simple de gérer les dépendances avant de compiler les définitions de type personnalisées - ajoutez simplement les noms d'assembly RequiredAssembliesau manifeste du module:
New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing
Charger à partir du disque avec using assembly ...
Si le chemin de l'assembly requis est connu, vous pouvez le charger au moment de l'analyse avec une using assemblyinstruction:
using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'
class FormsDependentClass
{
[Windows.Forms.NotifyIcon]$BallonTipIcon
}
Pour votre cas d'utilisation, ce dernier n'est pas très attrayant car vous auriez besoin de coder en dur l'assembly au lieu de simplement le charger à partir du GAC par son nom.
Pourquoi cela se produit-il en premier lieu?
Ce comportement peut être légèrement déroutant car tout le reste dans PowerShell n'est qu'une simple interprétation «une instruction à la fois».
La raison de cette exception est d'autoriser les scripts et les fonctions à encapsuler des types de paramètres personnalisés:
param(
[CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}
Sans cette compilation préemptive de CustomEnumTypeau moment de l'analyse, PowerShell ne serait pas en mesure d'offrir la saisie semi-automatique et la validation d'entrée pour l' -Optionargument de paramètre - car son type n'existerait pas