Question [Astuce]Cmt utiliser DebugView avec PS ?

Plus d'informations
il y a 17 ans 5 mois #2921 par Laurent Dardenne
J'ai trouvé cette astuce qui peut être intéressante pour débugger des scripts, ou tout du moins tracer son exécution dans une seconde fenêtre.
On utilise l'outil de SysInternal DebugView ( technet.microsoft.com/fr-fr/sysinternals/bb896647(en-us).aspx )
Qui permet de visualiser des messages envoyés de PowerShell vers DebugView.
[code:1]
#exécute DebugView
&\"G:\PS\debug\DebugView\Dbgview.exe\"
Start-Sleep 2
# On perd le focus de la console :/
#http://blogs.msdn.com/powershell/archive/2008/06/03/show-powershell-hide-powershell.aspx
#Hide-PowerShell
#Show-PowerShell

#envoi un message
[int] $level=1
[string] $category=\"Warning\"
[string] $message=\"Test\"
[System.Diagnostics.Debugger]::Log($level, $category, $message)

# Affiche le contenu de $A en chaine de caractères uniquement
$A=Dir
[System.Diagnostics.Debugger]::Log(2,\"Commentaire\", $A)

#Si on termine DebugView.
#\"Closes a process that has a user interface by sending a close message to its main window. \"
(get-process dbgview).CloseMainWindow()
#Dans ce cas l'exécution du code suivant ne pose pas de problème
[System.Diagnostics.Debugger]::Log(2,\"Commentaire\", $A)
[/code:1]
A noter que les appels à Debug.Write au sein du code d'une classe sont récupérés par DbgView.
Si on exécute le code suivant on peut se connecter à VS, en le sélectionnant
[code:1]
[System.Diagnostics.Debugger]::Launch()
[/code:1]
Dans ce cas le code de la classe ,compilé en debug, envoi les infos de trace sur VS( avec un projet actif) et sur DbgWiew
[code:1]
$DllPath=\"G:\PS\debug\AppDebugLog\AppDebugLog\bin\Debug\AppDebugLog.dll\"
[void][Reflection.Assembly]::LoadFile($DllPath)
$obj=new-object AppDebugLog.ClassTest
$obj.Affiche()
[/code:1]
Le code de test C# :

namespace AppDebugLog
{
public class ClassTest
{
public void Affiche(){
Console.WriteLine(\"Affichage par Console.Writeline\");
Debug.Write(3, \"Message via Debug.Write par la classe\");
}
}
}


Tutoriels PowerShell

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

Plus d'informations
il y a 17 ans 5 mois #2953 par Laurent Dardenne
Une autre possibilité autour de DbgView.

En utilisant une variable contrainte il est possible de tracer toutes les affectations d'une variable globale :
On crée une classe de contrainte personnalisé :
[code:1]
namespace PowerShell.Attributes
{
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class ValidateViewDebugAttribute : ValidateArgumentsAttribute
{

protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics)
{
PSVariable varInvocation=engineIntrinsics.SessionState.PSVariable.Get(\"Myinvocation\"«»);
InvocationInfo Values = varInvocation.Value as InvocationInfo;
if (arguments != null)
{
//Debug.Write(3, String.Format(\"Script:{0} ; Nom : {1} ; Valeur :{2}\", varInvocation.Value[\"ScriptName\"], varInvocation.Name, varInvocation.ToString()));
Debug.Write(3, String.Format(\"Valeur :{0} ; Type appelant({1}) ligne ({2}); Call : {3}\", arguments.ToString(),
Values.MyCommand.CommandType.ToString(),
Values.ScriptLineNumber,
Values.ScriptName + \".\" + Values.MyCommand.Name));
}
else
{
Debug.Write(3, String.Format(\"Valeur : $null ; Type appelant({0}) ligne ({1}); Call : {2}\", Values.MyCommand.CommandType.ToString(),
Values.ScriptLineNumber,
Values.ScriptName + \".\" + Values.MyCommand.Name));
}
}
}
}
[/code:1]
Qui ne fait qu'envoyer des traces au debugger et valide toujours la valeur en cours d'affectation.
Malheureusement le nom de la variable concernée par l'affectation ne peut être récupérée car cette méthode n'a pas à la connaître.

[code:1]
&\"G:\PS\debug\DebugView\Dbgview.exe\"

# Assembly sans nom fort, on doit préciser le chemin complet
$FullPath = \"VotreChemin\ValidateViewDebugAttribute.dll\"
[void][Reflection.Assembly]::LoadFile($FullPath)
[/code:1]
Ensuite on déclare 2 fonctions :
[code:1]
Function Add-ContrainteVariableDbgView([System.Management.Automation.PSVariable] $Var){
# Ajoute une contrainte de validation ViewDebug
# Cette contrainte affiche, dans le debuger, des inforamtions de trace lors de chaque modification
# du contenu de la variable $Var
#
# Renvoi -1 si la contrainte existe déjà ou le numéro d'index dans la collection Attributes de la variable $Var
#
if ($Var -eq $null)
{ throw (New-Object System.ArgumentException \"Le paramètre `$Var est à `$null.\"«») }

$Attribut=New-object \"PowerShell.Attributes.ValidateViewDebugAttribute\"
$Count=$Var.Attributes.Count
#Liste existante
if ($Count -ne 0)
{ $Index=$Var.Attributes.IndexOf($Attribut)
#Contrainte existante
if ( $Index -ne -1)
{
Write-Debug \"Une contrainte ViewDebug est déjà déclarée sur la variable `$$($Var.Name).\"
return -1
}
}
$Var.Attributes.add($Attribut)
Write-Debug \"Contrainte ViewDebug déclarée sur la variable `$$($Var.Name).\"
#L'index de la nouvelle entrée = $Var.Attributes.Count-1 c'est à dire l'ancienne valeur de $Count.
return $Count
}

Function Remove-ContrainteVariableDbgView([System.Management.Automation.PSVariable] $Var){
# Supprime une contrainte de validation ViewDebug s'il en existe une
if ($Var -eq $null)
{ throw (New-Object System.ArgumentException \"Le paramètre `$Var est à `$null.\"«») }

$Attribut=New-object \"PowerShell.Attributes.ValidateViewDebugAttribute\"
$Index=$Var.Attributes.IndexOf($Attribut)
#Contrainte existante
if ( $Index -ne -1)
{
$Var.Attributes.RemoveAt($Index)
Write-Debug \"Contrainte ViewDebug supprimé pour la variable `$$($Var.Name).\"
}
else {Write-Debug \"Aucune contrainte ViewDebug de déclarée sur la variable `$$($Var.Name).\"}
}
[/code:1]

Quelques tests sur le comportement de ces fonctions :
[code:1]
$X=10
$DefX=Get-Variable X
$DebugPreference=\"Continue\"
#Tests
Remove-ContrainteVariableDbgView $null
Add-ContrainteVariableDbgView $null

Remove-ContrainteVariableDbgView $DefX
#ou Remove-ContrainteVariableDbgView (Get-Variable X)
Add-ContrainteVariableDbgView $DefX
#Ou Add-ContrainteVariableDbgView (Get-Variable X)
Add-ContrainteVariableDbgView $DefX
Remove-ContrainteVariableDbgView $DefX
Add-ContrainteVariableDbgView $DefX
$DebugPreference=\"SilentlyContinue\"

#Ajoute une contrainte défini dans les API de PS
#$SMA=\"System.Management.Automation\"
#$ContraintedEtendu=New-object \"$SMA.ValidateRangeAttribute\" 2,15
#$DefX.Attributes.add($ContraintedEtendu)
[/code:1]
Visualisation des traces par l'affection d'une nouvelle valeur à $X :
[code:1]
$x=11

function Calcule {$global:«»x++}
calcule

# Fichier Test.ps1
#
# function Interne{$global:«»x++}
# function Interne{$global:«»x++;Calcule}
# $global:«»x++
# Interne
# Calcule
# $y=$global:«»X
# $global:«»x=$null
# #la variable globale X existe toujours seul son contenu est à affecté à $null
# $global:«»X=$y

.\Test.ps1

$x=$null
[/code:1]
Une fois déclaré la contrainte sur la variable,$X=10, voici les résultats obtenus dans le debugger.

Lors de l'appel à Add-ContrainteVariableDbgView

Valeur :10 ; Type appelant(Function) ligne (1); Call : .Add-ContrainteVariableDbgView: 3

Pour une affection dans la console : $x=11

Valeur :11 ; Type appelant(Script) ligne (0); Call : .: 3


Pour un accés au sein d'une fonction ( function Calcule {$global:x++}), appelée dans la console : Calcule

Valeur :12 ; Type appelant(Function) ligne (1); Call : .Calcule: 3


Pour un accés au sein du script externe Test.ps1 on obtient respectivement :

Valeur :13 ; Type appelant(ExternalScript) ligne (1); Call : .Test.ps1: 3
Valeur :14 ; Type appelant(Function) ligne (4); Call : G:\PS\debug\Test.ps1.Interne: 3
Valeur :15 ; Type appelant(Function) ligne (2); Call : G:\PS\debug\Test.ps1.Calcule: 3
Valeur :16 ; Type appelant(Function) ligne (5); Call : G:\PS\debug\Test.ps1.Calcule: 3
Valeur : $null ; Type appelant(ExternalScript) ligne (1); Call : .Test.ps1: 3
Valeur :16 ; Type appelant(ExternalScript) ligne (1); Call : .Test.ps1: 3
Valeur : $null ; Type appelant(Script) ligne (0); Call : .: 3

Pour tracer dans quelque script assez long et ne disposant pas de code debug il faut redéclarer dans chaque portée une contrainte sur la variable à tracer et la supprimer en fin de traitement, attention au clause return :
[code:1]
Add-ContrainteVariableDbgView (Get-variable xxx)
...
Remove-ContrainteVariableDbgView (Get-variable xxx)
[/code:1]
Cela peut aider dans quelque cas sinon mieux vaut investir dans un debugger ;-)

Tutoriels PowerShell

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

Plus d'informations
il y a 17 ans 5 mois #2954 par Laurent Dardenne
La dll de la contrainte.

La pièce jointe ValidateViewDebugAttribute.zip est absente ou indisponible

<br><br>Message édité par: Laurent Dardenne, à: 13/10/08 19:13

Tutoriels PowerShell

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

Plus d'informations
il y a 17 ans 2 mois #3577 par Laurent Dardenne
Lors d'une séance de debug on peut vouloir afficher le détail d'un objet voici une fonction pour ce faire :
[code:1]
function Write-ObjectProperties ($From, $PropertyName =\&quot;*\&quot;, [Switch] $Pipeline, [Switch] $Debug)
{ #Affiche le contenu de toutes les propriétés d'un objet
#
# $From : l'objet à interroger,
# $PropertyName : le nom de la propriété, ce nom peut contenir des jokers,
# $Pipeline : indique si le résultat est émis dans le pipeline
# $Debug : indique si le résultat est envoyé vers le debugger
#
#Exemples :
# $a=dir
# Write-Properties $a[-1]
# Write-Properties $a[-1] -Pipeline |%{Write-host $_ -fore DarkGreen}
# Res=Write-Properties $a[-1] -Debug -Pipeline

foreach ($p in Get-Member -In $From -MemberType *Property -Name $propertyName|Sort name)
{
$Result =\&quot;$($P.Name) : $($From.$($P.Name))\&quot;

if ( $Pipeline.IsPresent) {$result}
else {Write-Host $Result}
if ( $Debug.IsPresent)
{
#CommandLineParameters en PS v2 seulement
$Source=$MyInvocation.CommandLineParameters.\&quot;From\&quot;.ToString()
[int] $level=1
[string] $category=\&quot;WP-$Source\&quot;
#Inopérant si aucun débugger n'est actif
[System.Diagnostics.Debugger]::Log($level, $category, $Result)
}
}
}
[/code:1]
A notez que l'instruction $MyInvocation.CommandLineParameters n'est valide quà partir de la version 2 ctp2.

Tutoriels PowerShell

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

Plus d'informations
il y a 17 ans 1 mois #3872 par Laurent Dardenne
Une version corrigée prenant correctement en charge le pipeline :
[code:1]
Set-Alias -name $AliasName -value Write-Properties
function Write-Properties($From, $PropertyName =\&quot;*\&quot;,[Switch] $Passthru, [Switch] $Silently)
{ #Adaptation d'un script de J.Snoover
#Emet le contenu de toutes les propriétés d'un objet dans le pipeline, sur la console et sur un debugger actif
#
# $From : l'objet à interroger,
# $PropertyName : le nom de la propriété, ce nom peut contenir des jokers.
# C'est un tableau de string
# $Silently : N'émet plus les informations sur la console mais seulement vers le debugger actif
#
#Exemples :
# $a=dir
# Write-Properties $a[-1]
# Write-Properties $a[-1] -pass |%{Write-host $_ -fore DarkGreen}
# $Res=Write-Properties $a[-1] -pass
# wp $A[-1] Name,Extension,L*

begin
{
$IsDebuggerAttached=[System.Diagnostics.Debugger]::IsAttached
$CS=\&quot;Call : {0}\&quot; -F $MyInvocation.InvocationName
Write-Debug $CS

function WriteProperties{

if ($IsDebuggerAttached) # faire [System.Diagnostics.Debugger]::Launch()
{
$sbDbgWrite={[System.Diagnostics.Debug]::WriteLine($Args[0])} #Pour VS
[System.Diagnostics.Debug]::Indent()
}
else {$sbDbgWrite={[System.Diagnostics.Debug]::Write($Args[0])}} #Pour DbgView
#La chaine $CS est envoyé vers le debugger actif
&amp;$sbDbgWrite $CS

foreach ($P in Get-Member -In $From -MemberType *Property -Name $propertyName|Sort name)
{
$Result =\&quot;$($P.Name) : $($From.$($P.Name))\&quot;

#Affiche ou non sur la console.
#Dans tous les cas on affiche sur le débugger
if (!$Silently) {Write-Host $Result}

if ((get-Variable psversiontabl[e]) -ne $null)
{ #CommandLineParameters en PS v2 seulement
$Source=$MyInvocation.CommandLineParameters.\&quot;From\&quot;.ToString()
$Result=\&quot;$Source : $Result\&quot;
}
&amp;$sbDbgWrite $Result
if ($Passthru) {$Result}
}
if ($IsDebuggerAttached)
{[System.Diagnostics.Debug]::Unindent() }
}
}
process
{
if ($_)
{
WriteProperties -From $_ $PropertyName -Silently:$Silently
$_
}
}
end
{
if ($From)
{ WriteProperties -From $From $PropertyName -Silently:$Silently }
}
} #Write-Properties
[/code:1]
On peut utiliser soit dbgView soit VisualStudio (version Express ou &gt; ), dans ce cas l'affichage des propriétés est indenté.
L'utilisation de la classe System.Diagnostics.Debug nécessite moins de code.

Voir aussi
[code:1]
[System.Diagnostics.Debug]::Assert($xyz -ne $null)
[/code:1]
Si le test est faux le framework affiche une fenêtre de confirmation.
Le choix de \&quot;Ignorer\&quot; continue le script, \&quot;Abandonner\&quot; ferme la session PS et \&quot;Recommencer\&quot; permet de lancer un des debuggers recensés.<br><br>Message édité par: Laurent Dardenne, à: 17/02/09 16:37

Tutoriels PowerShell

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

Plus d'informations
il y a 17 ans 1 mois #4085 par Laurent Dardenne
Mise à jour de la fonction Write-Properties.
Ajout du switch Passthru permettant d'envoyer le résultat dans le pipeline.
On peut donc émettre le résultat sur la console et dans le debugger et/ou dans le pipeline.

Tutoriels PowerShell

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

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