Question [fonction]Création de constantes
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
Réduire
Plus d'informations
- Messages : 6302
- Remerciements reçus 68
il y a 15 ans 5 mois #3224
par Laurent Dardenne
Tutoriels PowerShell
[fonction]Création de constantes a été créé par Laurent Dardenne
PowerShell ne propose pas de notion de constante similaire aux langages de programmation statique à part de déclarer une variable avec \"Set-Variable -option constant\".
Dans ce cas les variables de type valeur : entier, string, boolean,etc seront effectivement en lecture seule tout comme un objet, on ne peut pas lui affecter un nouvel objet, en revanche pour celui-ci ces propriétés restent modifiables.
On peut simplifier la gestion des constantes en utilisant une HashTable mais ces valeurs restent en lecture/écriture.
J'ai eu besoin pour les scripts de gestion des API Win32 d'une structure déclarant des constantes sans avoir à déclarer autant ligne, avec Set-Variable, que de constantes nécessaires. De plus la déclaration d'une variable se fait dans la portée courante.
La solution ci-jointe couple l'usage du cmdlet Set-Variable à la création d'un objet personnalisé contenant des propriétés en lecture seule.
[code:1]
function Set-cstApiWindows
{ #Crée une variable constante dans la portée de l'appelant, elle est nommée cApiWindows
#Pour Get-CallStack et parseStack voir le post suivant :
# powershell-scripting.com/index.php?optio...id=2506&catid=14
#Vérifie la présence de lexistence de la variable cApiWindows dans le provider de variable.
if ( !(Test-Path Variable:cApiWindows) )
{
Write-Debug \"La variable cApiWindows n'existe pas.`r`nPile d'appel : $(ParseStack $(Get-CallStack))\"
#On utilise une hastable pour déclarer une constant qui est une association entre un nom, un type et une valeur.
#ici le type est int32
$APIConst=@{
SW_HIDE = 0;
SW_SHOWNORMAL = 1;
SW_NORMAL = 1;
SW_SHOWMINIMIZED = 2;
SW_SHOWMAXIMIZED = 3;
SW_MAXIMIZE = 3;
SW_SHOWNOACTIVATE = 4;
SW_SHOW = 5;
SW_MINIMIZE = 6;
SW_SHOWMINNOACTIVE = 7;
SW_SHOWNA = 8;
SW_RESTORE = 9;
SW_SHOWDEFAULT = 10;
SW_MAX = 10;
SC_SIZE = 61440;
SC_MOVE = 61456;
SC_MINIMIZE = 61472;
SC_MAXIMIZE = 61488;
SC_RESTORE = 61728;
SC_SEPARATOR = 61455;
SC_CLOSE = 61536;
SC_DEFAULT = 61792;
SWP_NOSIZE = 1;
SWP_NOMOVE = 2;
SWP_NOZORDER = 4;
SWP_NOREDRAW = 8;
SWP_NOACTIVATE = 16;
SWP_FRAMECHANGED = 32;
SWP_SHOWWINDOW = 64;
SWP_HIDEWINDOW = 128;
SWP_NOCOPYBITS = 256;
SWP_NOOWNERZORDER = 512;
SWP_NOSENDCHANGING = 1204;
SWP_DRAWFRAME = 32; #SWP_FRAMECHANGED
SWP_NOREPOSITION =512; #SWP_NOOWNERZORDER
SWP_DEFERERASE = 8192;
SWP_ASYNCWINDOWPOS = 16384;
HWND_TOP=0
HWND_BOTTOM=1
HWND_TOPMOST=-1
HWND_NOTOPMOST=-2
MF_STRING = 0
MF_BITMAP = 4
MF_OWNERDRAW = 256
MF_ENABLED =0
MF_GRAYED =1
MF_DISABLED =2
MF_BYCOMMAND = 0
MF_BYPOSITION = 1024
}#APIConst
$obj= New-Object PSObject
$APIConst.GetEnumerator()|`
%{ #Construit le code de la création d'un objet ayant des propriétés en lecture seule
$C1=\"`$obj| add-member -memberType Scriptproperty\"
$C2=\"-Name $($_.Key)\"
#on force le type
$C3=\"-value {[int32] $($_.Value)}\"
$C4=\"-SecondValue {Throw `\"La propriété $($_.Key) est en lecture seule.`\"}\"
# facilite la lecture en lieu et place d'une here-string qui est parfois malaisée à mettre au point
$Code=\"$C1 $C2 $C3 $C4\"
#Exécute le code créé
invoke-expression $Code
}#Foreach
#On crée la variable dans la portée de l'appelant,
#on évite une globale au prix de possible constructions fréquentes
Set-Variable cApiWindows -value $Obj -option constant -scope 1
}
else
{ Write-Debug \"La variable cApiWindows existe déjà. `r`nPile d'appel : $(ParseStack $(Get-CallStack))\" }
}
[/code:1]
Si vous utilisez les API windows au sein d'un script une des premières lignes doit être l'appel de cette fonction.
Cela évite d'une part d'avoir une liste de variable globales en expansion et d'autre part de s'assurer que les valeurs devant être constantes le sont et le reste.
Il va s'en dire que si vous utilisez de nombreuses constantes de domaines différent, il vous faudra adapter cette fonction pour généraliser son usage.
Un des avantages que j'y trouve ici est qu'on ne déclare qu'une seule variable ce qui ne nécessite qu'un seul test.
On peut très bien faire:
[code:1]
function Set-cstApiWindows
{
Set-Variable SW_HIDE -value 0 -option constant -scope 1
Set-Variable SW_SHOWNORMAL -value 1 -option constant -scope 1
Set-Variable SW_NORMAL -value 1 -option constant -scope 1
Set-Variable SW_SHOWMINIMIZED -value 2 -option constant -scope 1
#etc
}
[/code:1]
Dans cas, lors d'appels répétés, la surchage du garbage collector peut ne pas être négligeable.
Si vous avez une meilleure idée et/ou des questions n'hésitez pas à me les communiquer.
Dans ce cas les variables de type valeur : entier, string, boolean,etc seront effectivement en lecture seule tout comme un objet, on ne peut pas lui affecter un nouvel objet, en revanche pour celui-ci ces propriétés restent modifiables.
On peut simplifier la gestion des constantes en utilisant une HashTable mais ces valeurs restent en lecture/écriture.
J'ai eu besoin pour les scripts de gestion des API Win32 d'une structure déclarant des constantes sans avoir à déclarer autant ligne, avec Set-Variable, que de constantes nécessaires. De plus la déclaration d'une variable se fait dans la portée courante.
La solution ci-jointe couple l'usage du cmdlet Set-Variable à la création d'un objet personnalisé contenant des propriétés en lecture seule.
[code:1]
function Set-cstApiWindows
{ #Crée une variable constante dans la portée de l'appelant, elle est nommée cApiWindows
#Pour Get-CallStack et parseStack voir le post suivant :
# powershell-scripting.com/index.php?optio...id=2506&catid=14
#Vérifie la présence de lexistence de la variable cApiWindows dans le provider de variable.
if ( !(Test-Path Variable:cApiWindows) )
{
Write-Debug \"La variable cApiWindows n'existe pas.`r`nPile d'appel : $(ParseStack $(Get-CallStack))\"
#On utilise une hastable pour déclarer une constant qui est une association entre un nom, un type et une valeur.
#ici le type est int32
$APIConst=@{
SW_HIDE = 0;
SW_SHOWNORMAL = 1;
SW_NORMAL = 1;
SW_SHOWMINIMIZED = 2;
SW_SHOWMAXIMIZED = 3;
SW_MAXIMIZE = 3;
SW_SHOWNOACTIVATE = 4;
SW_SHOW = 5;
SW_MINIMIZE = 6;
SW_SHOWMINNOACTIVE = 7;
SW_SHOWNA = 8;
SW_RESTORE = 9;
SW_SHOWDEFAULT = 10;
SW_MAX = 10;
SC_SIZE = 61440;
SC_MOVE = 61456;
SC_MINIMIZE = 61472;
SC_MAXIMIZE = 61488;
SC_RESTORE = 61728;
SC_SEPARATOR = 61455;
SC_CLOSE = 61536;
SC_DEFAULT = 61792;
SWP_NOSIZE = 1;
SWP_NOMOVE = 2;
SWP_NOZORDER = 4;
SWP_NOREDRAW = 8;
SWP_NOACTIVATE = 16;
SWP_FRAMECHANGED = 32;
SWP_SHOWWINDOW = 64;
SWP_HIDEWINDOW = 128;
SWP_NOCOPYBITS = 256;
SWP_NOOWNERZORDER = 512;
SWP_NOSENDCHANGING = 1204;
SWP_DRAWFRAME = 32; #SWP_FRAMECHANGED
SWP_NOREPOSITION =512; #SWP_NOOWNERZORDER
SWP_DEFERERASE = 8192;
SWP_ASYNCWINDOWPOS = 16384;
HWND_TOP=0
HWND_BOTTOM=1
HWND_TOPMOST=-1
HWND_NOTOPMOST=-2
MF_STRING = 0
MF_BITMAP = 4
MF_OWNERDRAW = 256
MF_ENABLED =0
MF_GRAYED =1
MF_DISABLED =2
MF_BYCOMMAND = 0
MF_BYPOSITION = 1024
}#APIConst
$obj= New-Object PSObject
$APIConst.GetEnumerator()|`
%{ #Construit le code de la création d'un objet ayant des propriétés en lecture seule
$C1=\"`$obj| add-member -memberType Scriptproperty\"
$C2=\"-Name $($_.Key)\"
#on force le type
$C3=\"-value {[int32] $($_.Value)}\"
$C4=\"-SecondValue {Throw `\"La propriété $($_.Key) est en lecture seule.`\"}\"
# facilite la lecture en lieu et place d'une here-string qui est parfois malaisée à mettre au point
$Code=\"$C1 $C2 $C3 $C4\"
#Exécute le code créé
invoke-expression $Code
}#Foreach
#On crée la variable dans la portée de l'appelant,
#on évite une globale au prix de possible constructions fréquentes
Set-Variable cApiWindows -value $Obj -option constant -scope 1
}
else
{ Write-Debug \"La variable cApiWindows existe déjà. `r`nPile d'appel : $(ParseStack $(Get-CallStack))\" }
}
[/code:1]
Si vous utilisez les API windows au sein d'un script une des premières lignes doit être l'appel de cette fonction.
Cela évite d'une part d'avoir une liste de variable globales en expansion et d'autre part de s'assurer que les valeurs devant être constantes le sont et le reste.
Il va s'en dire que si vous utilisez de nombreuses constantes de domaines différent, il vous faudra adapter cette fonction pour généraliser son usage.
Un des avantages que j'y trouve ici est qu'on ne déclare qu'une seule variable ce qui ne nécessite qu'un seul test.
On peut très bien faire:
[code:1]
function Set-cstApiWindows
{
Set-Variable SW_HIDE -value 0 -option constant -scope 1
Set-Variable SW_SHOWNORMAL -value 1 -option constant -scope 1
Set-Variable SW_NORMAL -value 1 -option constant -scope 1
Set-Variable SW_SHOWMINIMIZED -value 2 -option constant -scope 1
#etc
}
[/code:1]
Dans cas, lors d'appels répétés, la surchage du garbage collector peut ne pas être négligeable.
Si vous avez une meilleure idée et/ou des questions n'hésitez pas à me les communiquer.
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Grégory
- Hors Ligne
- Membre senior
Réduire
Plus d'informations
- Messages : 49
- Remerciements reçus 0
il y a 15 ans 5 mois #3226
par Grégory
Réponse de Grégory sur le sujet Re:[fonction]Création de constantes
Une petite question
tu utilises :
[code:1]$C2=\"-Name $($_.Key)\"[/code:1]
j'aurais utilisé :
[code:1]$C2=\"-Name \" + $_.name[/code:1]
il y a une raison à ça ?
tu utilises :
[code:1]$C2=\"-Name $($_.Key)\"[/code:1]
j'aurais utilisé :
[code:1]$C2=\"-Name \" + $_.name[/code:1]
il y a une raison à ça ?
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
Réduire
Plus d'informations
- Messages : 6302
- Remerciements reçus 68
il y a 15 ans 5 mois #3227
par Laurent Dardenne
Tutoriels PowerShell
Réponse de Laurent Dardenne sur le sujet Re:[fonction]Création de constantes
Nostra écrit:
C'est l'affichage de Powershell, via ETS je pense, qui considére qu'il existe les membres nommés Name et Value.
Keys contient les noms de clés, colonne Name, et Values les valeurs des clés, colonne Value.
On peut donc accéder séparément aux deux collections.
L'affichage d'un objet ne correspond pas toujours aux propriétés qu'il contient réellement, voir par exemple les propriétés calculées.<br><br>Message édité par: Laurent Dardenne, à: 13/11/08 11:37
Oui, une hastable n'a pas de membre nommé Name, ici une propriété.j'aurais utilisé :
[code:1]$C2=\"-Name \" + $_.name[/code:1]
il y a une raison à ça ?
C'est l'affichage de Powershell, via ETS je pense, qui considére qu'il existe les membres nommés Name et Value.
Keys contient les noms de clés, colonne Name, et Values les valeurs des clés, colonne Value.
On peut donc accéder séparément aux deux collections.
L'affichage d'un objet ne correspond pas toujours aux propriétés qu'il contient réellement, voir par exemple les propriétés calculées.<br><br>Message édité par: Laurent Dardenne, à: 13/11/08 11:37
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Grégory
- Hors Ligne
- Membre senior
Réduire
Plus d'informations
- Messages : 49
- Remerciements reçus 0
il y a 15 ans 5 mois #3228
par Grégory
Réponse de Grégory sur le sujet Re:[fonction]Création de constantes
J'ai un peu de mal à comprendre ...
Par exemple :
[code:1]$apiconst|%{$_.keys}[/code:1]
me renvoit la même chose que :
[code:1]$apiconst.getenumerator()|%{$_.name}[/code:1]
de même que :
[code:1]$apiconst.getenumerator()|%{$_.key}[/code:1]
Par exemple :
[code:1]$apiconst|%{$_.keys}[/code:1]
me renvoit la même chose que :
[code:1]$apiconst.getenumerator()|%{$_.name}[/code:1]
de même que :
[code:1]$apiconst.getenumerator()|%{$_.key}[/code:1]
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
Réduire
Plus d'informations
- Messages : 6302
- Remerciements reçus 68
il y a 15 ans 5 mois #3230
par Laurent Dardenne
Tutoriels PowerShell
Réponse de Laurent Dardenne sur le sujet Re:[fonction]Création de constantes
Nostra écrit:
Pour
[code:1] $apiconst|%{$_.keys}[/code:1]
On adresse la collection de clé (type=KeyCollection), chaque entrée d'une hashtable est de type DictionaryEntry qui elle porte bien les propriétés Name et Key.
Dans les cas où on utilise l'énumérateur on adresse ici chaque objet de la collection (type=DictionaryEntry).
Dans les fichiers .ps1xml, Il n'y a pas de référence à la classe hastable mais ils en existent pour DictionaryEntry:
[code:1]
#types.ps1xml
<Type>
<Name>System.Collections.DictionaryEntry</Name>
<Members>
<AliasProperty>
<Name>Name</Name>
<ReferencedMemberName>Key</ReferencedMemberName>
</AliasProperty>
</Members>
</Type>
[/code:1]
et
[code:1]
#dotnettypes.format.ps1xml
<View>
<Name>System.Collections.DictionaryEntry</Name>
<ViewSelectedBy>
<TypeName>Deserialized.System.Collections.DictionaryEntry</TypeName>
<TypeName>System.Collections.DictionaryEntry</TypeName>
</ViewSelectedBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<ListItem>
<PropertyName>Name</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Value</PropertyName>
</ListItem>
</ListItems>
</ListEntry>
</ListEntries>
</ListControl>
</View>
[/code:1]
Si tu fait
[code:1]
$a=$APIConst.GetEnumerator()|% {$_}
$a[0]|gm
[/code:1]
Tu retrouves l'alias Name et lors de l'affichage tu retrouves la colonne Name pointant sur la propriété Value.
Je pense que l'affichage de
[code:1]
$APIConst
[/code:1]
Pointe, pour chaque entrée, sur la vue citée plus haut.
Comment le vérifier ? Modifier le fichier de type et le recharger. Attention ces fichiers sont signés et je n'ai pas essayé d'en charger sans signature, à toi l'honneur.
C'est plus clair ?
[edit]
La confusion provient du fait que je parle des classes (POO) et pas des classes (type) transformées par PowerShell, cf.ETS (Scripting)<br><br>Message édité par: Laurent Dardenne, à: 13/11/08 14:05
Oui effectivement j'ai oublié de parler de la classe DictionaryEntry.J'ai un peu de mal à comprendre ...
Pour
[code:1] $apiconst|%{$_.keys}[/code:1]
On adresse la collection de clé (type=KeyCollection), chaque entrée d'une hashtable est de type DictionaryEntry qui elle porte bien les propriétés Name et Key.
Dans les cas où on utilise l'énumérateur on adresse ici chaque objet de la collection (type=DictionaryEntry).
Dans les fichiers .ps1xml, Il n'y a pas de référence à la classe hastable mais ils en existent pour DictionaryEntry:
[code:1]
#types.ps1xml
<Type>
<Name>System.Collections.DictionaryEntry</Name>
<Members>
<AliasProperty>
<Name>Name</Name>
<ReferencedMemberName>Key</ReferencedMemberName>
</AliasProperty>
</Members>
</Type>
[/code:1]
et
[code:1]
#dotnettypes.format.ps1xml
<View>
<Name>System.Collections.DictionaryEntry</Name>
<ViewSelectedBy>
<TypeName>Deserialized.System.Collections.DictionaryEntry</TypeName>
<TypeName>System.Collections.DictionaryEntry</TypeName>
</ViewSelectedBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<ListItem>
<PropertyName>Name</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Value</PropertyName>
</ListItem>
</ListItems>
</ListEntry>
</ListEntries>
</ListControl>
</View>
[/code:1]
Si tu fait
[code:1]
$a=$APIConst.GetEnumerator()|% {$_}
$a[0]|gm
[/code:1]
Tu retrouves l'alias Name et lors de l'affichage tu retrouves la colonne Name pointant sur la propriété Value.
Je pense que l'affichage de
[code:1]
$APIConst
[/code:1]
Pointe, pour chaque entrée, sur la vue citée plus haut.
Comment le vérifier ? Modifier le fichier de type et le recharger. Attention ces fichiers sont signés et je n'ai pas essayé d'en charger sans signature, à toi l'honneur.
C'est plus clair ?
[edit]
La confusion provient du fait que je parle des classes (POO) et pas des classes (type) transformées par PowerShell, cf.ETS (Scripting)<br><br>Message édité par: Laurent Dardenne, à: 13/11/08 14:05
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Grégory
- Hors Ligne
- Membre senior
Réduire
Plus d'informations
- Messages : 49
- Remerciements reçus 0
il y a 15 ans 5 mois #3235
par Grégory
Réponse de Grégory sur le sujet Re:[fonction]Création de constantes
Bon j'y suis allé un peu à l'aveuglette
donc en modifiant les 2 fichiers y a pas de problème et voilà à quoi j'arrive en modifiant :
<Name>System.Collections.DictionaryEntry</Name>
par
<Name>Hashtable</Name>
et
<TypeName>System.Collections.DictionaryEntry</TypeName>
par
<TypeName>Hashtable</TypeName>
img46.imageshack.us/my.php?image=imp2nu5.jpg
Bon maintenant je suis pas assez calé pour en tirer des conclusions
donc en modifiant les 2 fichiers y a pas de problème et voilà à quoi j'arrive en modifiant :
<Name>System.Collections.DictionaryEntry</Name>
par
<Name>Hashtable</Name>
et
<TypeName>System.Collections.DictionaryEntry</TypeName>
par
<TypeName>Hashtable</TypeName>
img46.imageshack.us/my.php?image=imp2nu5.jpg
Bon maintenant je suis pas assez calé pour en tirer des conclusions
Connexion ou Créer un compte pour participer à la conversation.
Temps de génération de la page : 0.083 secondes
- Vous êtes ici :
- Accueil
- forum
- PowerShell
- Contributions à la communauté
- [fonction]Création de constantes