Question [fonction]Création de constantes

Plus d'informations
il y a 15 ans 5 mois #3224 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.

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 5 mois #3226 par Grégory
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 ?

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 5 mois #3227 par Laurent Dardenne
Nostra écrit:

j'aurais utilisé :
[code:1]$C2=\"-Name \" + $_.name[/code:1]
il y a une raison à ça ?

Oui, une hastable n'a pas de membre nommé Name, ici une propriété.
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.

Plus d'informations
il y a 15 ans 5 mois #3228 par Grégory
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]


:S

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 5 mois #3230 par Laurent Dardenne
Nostra écrit:

J'ai un peu de mal à comprendre ...

Oui effectivement j'ai oublié de parler de la classe DictionaryEntry.
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
&lt;Type&gt;
&lt;Name&gt;System.Collections.DictionaryEntry&lt;/Name&gt;
&lt;Members&gt;
&lt;AliasProperty&gt;
&lt;Name&gt;Name&lt;/Name&gt;
&lt;ReferencedMemberName&gt;Key&lt;/ReferencedMemberName&gt;
&lt;/AliasProperty&gt;
&lt;/Members&gt;
&lt;/Type&gt;
[/code:1]
et
[code:1]
#dotnettypes.format.ps1xml
&lt;View&gt;
&lt;Name&gt;System.Collections.DictionaryEntry&lt;/Name&gt;
&lt;ViewSelectedBy&gt;
&lt;TypeName&gt;Deserialized.System.Collections.DictionaryEntry&lt;/TypeName&gt;
&lt;TypeName&gt;System.Collections.DictionaryEntry&lt;/TypeName&gt;
&lt;/ViewSelectedBy&gt;
&lt;ListControl&gt;
&lt;ListEntries&gt;
&lt;ListEntry&gt;
&lt;ListItems&gt;
&lt;ListItem&gt;
&lt;PropertyName&gt;Name&lt;/PropertyName&gt;
&lt;/ListItem&gt;
&lt;ListItem&gt;
&lt;PropertyName&gt;Value&lt;/PropertyName&gt;
&lt;/ListItem&gt;
&lt;/ListItems&gt;
&lt;/ListEntry&gt;
&lt;/ListEntries&gt;
&lt;/ListControl&gt;
&lt;/View&gt;
[/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.

Plus d'informations
il y a 15 ans 5 mois #3235 par Grégory
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 :

&lt;Name&gt;System.Collections.DictionaryEntry&lt;/Name&gt;

par

&lt;Name&gt;Hashtable&lt;/Name&gt;


et

&lt;TypeName&gt;System.Collections.DictionaryEntry&lt;/TypeName&gt;

par

&lt;TypeName&gt;Hashtable&lt;/TypeName&gt;

img46.imageshack.us/my.php?image=imp2nu5.jpg

Bon maintenant je suis pas assez calé pour en tirer des conclusions :laugh:

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.083 secondes
Propulsé par Kunena