Question Génération automatique des exceptions d'1 méthode

Plus d'informations
il y a 15 ans 5 mois #3248 par Laurent Dardenne
Suite à ce post
exca écrit:

Quelles genre d'exceptions faut-il gérer ? D'ailleurs quand on appelle une commande il y a moyen de savoir quelle Exception il y a trap ?

Question intéressante. Le problème était d'automatiser, à partir des infos de MSDN, l'interrogation, la récupération des exceptions et la génération du code.
A part lire MSDN et coder manuellement, comment faire ???

Eh bien en creusant le sujet, j'ai trouvé une solution basée sur le WebService associé à MSDN : MSDN/TechNet Publishing System
Dans un premier temps, on ne réinvente pas la roue, on utilise MSDNman . Ce programme en ligne de commande encapsule les appels au WebService et peut générer un fichier XML contenant l'intégralité d'une page MSDN.

\"Reste\" ensuite à parser à l'aide d'XPath le fichier reçu (n'étant un pas un expert dans ce domaine cette partie pourrait être grandement améliorée).
On récupère donc le nom et le message associé de toutes les exceptions d'une méthode. Une fois ceci fait on peut générer le code de gestion de ces exceptions.

Exemple pour OpenRemoteBaseKey
[code:1]
#Interrogation de MSDN
$Excep=Get-MSDNExceptionMembers \"Microsoft.Win32.RegistryKey.OpenRemoteBaseKey\" \"$Pwd\OpenRemoteBaseKey.XML\"
#Genération du code pour PowerShell
Convert-ExceptionToCode $Excep \"PS\"
[/code:1]
Ce qui nous donne le code suivant :
[code:1]
#Microsoft.Win32.RegistryKey.OpenRemoteBaseKey ( m?thode )
trap
{
$TypeName=$_.Exception.GetType().FullName
switch ($TypeName)
{
\"System.ArgumentException\" {throw \"hKey n'est pas valide.\"}
\"System.IO.IOException\" {throw \"machineName est introuvable.\"}
\"System.ArgumentNullException\" {throw \"machineName est nullNothingnullptrune r?f?rence null (Nothing en Visual Basic). \"}
\"System.Security.SecurityException\" {throw \"L'utilisateur ne dispose pas des autorisations n?cessaires pour ex?cuter cette op?ration. \"}
\"System.UnauthorizedAccessException\" {throw \"L'utilisateur ne dispose pas des droits d'acc?s ? la base de registres appropri?s.\"}
default { Write-Debug \"Re Trown\"; Throw new-object $TypeName ($_.Message)}

}
}
# Insérez votre code
[/code:1]
Le code à insérer pouvant être :
[code:1]
$regKey = [microsoft.win32.registrykey]::openremotebasekey($type, $serveur)
[/code:1]

Les 2 scripts :
[code:1]
function Get-MSDNExceptionMembers([String] $Identifier=$(throw \"Le nom de l'identifieur à interroger doit être renseigné.\"«»),
[string] $FileName=$(throw \"Le nom de fichier doit être renseigné.\"«»),
[String] $Langue=\"fr-fr\",
[String] $Langage=\"c#\"«»)
{
#Get-MSDNExceptionMembers
# V1.0
# Laurent Dardenne, 16/11/2008
#
#Utilise le WebService de MSDN.
#On interroge un identifieur d'une classe .NET afin de récupérer la section Exception.
#Ce qui permet par la suite de générer un bloc de code de gestion des exceptions
#Le langage cible peut être PowerShell,C#,VB,Delphi,...

# MTPS Content Service
# services.msdn.microsoft.com/ContentServices/ContentService.asmx
# xmlfr.org/w3c/TR/xpath
#
# Xpath quick reference www.stylusstudio.com/docs/v62/d_xpath15.html
# Spec Fr : xmlfr.org/w3c/TR/xpath
#
# SketchPath is a free XPath Editor and XML analysis and testing tool : pgfearo.googlepages.com/downloads


#Exemple:
# $IdentifierMSDN=\"Microsoft.Win32.RegistryKey.OpenRemoteBaseKey\"
# $Result=Get-MSDNExceptionMembers $IdentifierMSDN \"$Pwd\OpenRemoteBasKey.XML\"
# $Result.MemberName
# Microsoft.Win32.RegistryKey.OpenRemoteBaseKey ( m?thode )
# $Result.Exceptions
#
# NameSpace Nom Texte
#
---
# System ArgumentException hKey n'est pas valide.
# System.IO IOException machineName est introuvable.
# System ArgumentNullException machineName est nullNothingnullptrun...
# System.Security SecurityException L'utilisateur ne dispose pas des aut...
# System UnauthorizedAccessException L'utilisateur ne dispose pas des dro...
#
#La page de code n'est pas géré.
#Le texte du message est brut de fonderie. Il sera souvent à modifier manuellement. De plus les noms d'arguments
# ne sont pas différenciés dans le texte du message.
# Les fichiers temporaires ( %Temp%) ne sont pas supprimés automatiquement.
#
#Par exemple le message suivant :
# \"machineName est nullNothingnullptrune r?f?rence null (Nothing en Visual Basic).\"
#devrait être
# \"$machineName est une référence null.\"

# Get-MSDNExceptionMembers \"Microsoft.Win32.RegistryKey.OpenRemoteBaseKey\" \"$Pwd\OpenRemoteBaseKey.XML\"
#
# --- La page de la methode OpenSubKey ne contient pas d'informations, car elle propose des surcharges
# --- On doit donc sélectionne une des ces surcharges
# Get-MSDNExceptionMembers \"Microsoft.Win32.RegistryKey.OpenSubKey\" \"$Pwd\OpenSubKey.XML\"
#
# --- Recherche la surcharge OpenSubKey(String)
# Get-MSDNExceptionMembers \"z9f66s0a\" \"$Pwd\OpenSubKey.XML\"
#
#
# Get-MSDNExceptionMembers \"Microsoft.win32.registrykey.ToString\" \"$Pwd\ToString.XML\"
#
# --- erreur dans le nom de l'identifieur, error 2; Pas de réseau = error 2
# Get-MSDNExceptionMembers \"System.IO.FileAcess\" \"$Pwd\FileAccess.xml\"
#
# --- nom de culture erroné
# Get-MSDNExceptionMembers \"System.IO.FileAccess\" \"$Pwd\FileAccess.xml\" \"test\"
#
# --- L'identifieur est une énumération, dans ce cas il n'y a pas d'intérêt à appeler le service
# Get-MSDNExceptionMembers \"System.IO.FileAccess\" \"$Pwd\FileAccess.xml\"
#
# --- fonction Sendmessage Win32 (uniquement sur MSDN US)
# Get-MSDNExceptionMembers ms644950 \"$Pwd\SendMessage.xml\"

#$Excp=Get-MSDNExceptionMembers \"Microsoft.Win32.RegistryKey.OpenRemoteBaseKey\" \"$Pwd\OpenRemoteBaseKey.XML\"
#$Excp=Get-MSDNExceptionMembers \"System.IO.TextReader.ReadLine\" \"$Pwd\TxtRead.XML\"

function get-xpn ($text)
{ #Crée un naviguateur pour une recherche XPath
#http://www.microsoft.com/technet/scriptcenter/topics/winpsh/payette3.mspx
$rdr = [System.IO.StringReader] $text
$trdr = [system.io.textreader]$rdr
$xpdoc = [System.XML.XPath.XPathDocument] $trdr
$xpdoc.CreateNavigator()
}

function ParseXml([String] $FileName)
{ #Récupération des lignes de la déclaration XML issu de MsdnMan
#L'extaction XML contient quelles lignes parasites.
$IsXml=$False
switch -file $FileName
{
#Début de la section XML
\"<?xml version=`\"1.0`\" encoding=`\"ibm850`\"?>\" {$IsXml=$True;$_;continue}
#Fin de la section XML
\"</response>\" {$IsXml=$False;$_;break}

default {if ($IsXml)
#On traite les lignes si on se trouve dans la section XML
{$_}
}
}
}

#
Vérification de la validité des paramètres
trap [System.Management.Automation.MethodInvocationException]
{throw \"Le nom de culture '$Langue' n'est pas pris en charge.\"}
&{$null=[System.Globalization.CultureInfo]::GetCultureInfo($Langue)}

trap [System.Management.Automation.PSInvalidCastException]
{Continue}
&{
if ($Identifier.Contains(\".\"«»))
{
[type] $T=$Identifier
$Name=$null;
if ($T.IsSubclassOf([System.Enum]))
{$Name=\"Une énumération\"}
elseif ($T.IsSubclassOf([System.EventHandler]))
{$Name=\"Un délégué\"}
elseif ($T.IsSubclassOf([System.Exception]))
{$Name=\"Une exception\"}
if ($Name -ne $null)
{throw \"$Name ne déclare pas d'exception.\"}
}
}

$TempFile = [IO.Path]::GetTempFileName()

Write-Debug (\"Membre : {0}\" -F $Identifier)
Write-Debug (\"Fichier temporaire : {0}\" -F $TempFile)
Write-Debug (\"Fichier XML : {0}\" -F $FileName)
Write-Debug (\"Langue : {0}\" -F $Langue)
Write-Debug (\"Langage : {0}\" -F $Langage)

#
Appel du WebService
.\msdnman.exe $Identifier -loc $Langue -language $Langage -Nologo -XML|Set-Content \"$TempFile\" -Encoding Ascii
if ($LASTEXITCODE -ne 0)
{throw (\"Erreur {0} lors de la récupération des informations par MsdnMan.\" -F $LASTEXITCODE) }
#Erreur 2 pas de reseau

Write-Debug \"On parse fichier texte `\"$TempFile`\" dans le fichier XML `\"$FileName`\"\"
ParseXml \"$TempFile\"|Set-Content \"$FileName\" -Encoding Ascii

[xml]$Xml= get-content $FileName -ReadCount 0 -Encoding Ascii

#
Préparation de la naviguation Xml-XPath
$Navigateur = get-xpn $xml.psbase.outerXML

#Ajoute les espaces de nom utilisés par le fichier XML.
$xmlnsManager = new-object System.Xml.XmlNamespaceManager ($Navigateur.NameTable)
$xmlnsManager.AddNamespace(\"mtps\", \"msdn2.microsoft.com/mtps\"«»);
$xmlnsManager.AddNamespace(\"xhtml\", \"www.w3.org/1999/xhtml\"«»);
$xmlnsManager.AddNamespace(\"asp\", \"msdn2.microsoft.com/asp\"«»);

#
Création du résultat,
# première étape: récupération du nom de l'identifieur
#
#Retrouve l'identifieur recherché, c'est à dire le nom de la classe
$PMain='/response/primary/xhtml:div/mtps:MemberLink'
$NodeMemberLink=$Navigateur.Select($PMain,$xmlnsManager)
if ($NodeMemberLink.MoveNext() )
{$Classe=$NodeMemberLink.Current.GetAttribute(\"Target\",\"\"«»)}

#Retrouve le complément du nom de l'identifieur recherché, c'est à dire le nom du membre
$PMain='/response/primary/xhtml:div/xhtml:div[@class=\"title\"]'
$NodeMemberLink=$Navigateur.Select($PMain,$xmlnsManager)
if ($NodeMemberLink.MoveNext() )
{$Methode=$NodeMemberLink.Current.Value}

#Construit l'objet, contenant les informations sur exceptions,
#et que l'on enverra dans le pipeline
$Objet=1|Select MemberName,Exceptions
$null=$Methode -match \"\.(.*)\.(\w+),(.*)$\"
#Reconstruit le nom de l'identifieur,
#ex : System.IO.TextReader.ReadLine ( m?thode )
$Objet.MemberName=\"$($Classe.Substring(12))`.$($Matches[2]) `($($Matches[3])`)\"
$Objet.Exceptions=@()
$Objet.Psobject.TypeNames[0] = \"ExceptionInfos\"

#
Seconde étape: récupération des informations sur les exceptions ---
#Pour la relecture on décompose le chemin de la requête
#path MainSection
$PMain='/response/primary/xhtml:div/xhtml:div[@id=\"mainSection\"]'
#path Exceptions
$PExceptions='/xhtml:div/mtps:CollapsibleArea[@Title=\"Exceptions\"]'
#path Table/Tr, contient la liste des exceptions de l'API interrogée
$PTableTr='/xhtml:div/xhtml:table/xhtml:tr'
$Recherche=$PMain+$PExceptions+$PTableTr;

#récupère les exceptions déclarées via un itérateur
$NodesException=$Navigateur.Select($Recherche,$xmlnsManager)
Write-Debug (\"NodesException.count : {0}\" -F $NodesException.Count)
if (Test-Path Variable:$NodesException)
{
Write-Debug \"Affiche les liens possibles\"
Write-host \"Recherchez dans la liste suivante`r`nIdentifier Nom\"
foreach ($Node in $XML.Response.Links.Links.Link) {Write-host (\"{0} : {1}\" -F $Node.contentid.\"#text\",$Node.assetid)}
Throw \"La section Exception n'est pas renseigné pour le membre $Identifier\"
}
#Index de début dans le tableau des exceptions
$i=2
$cAssetID=\"AssetId:T%3a\"
#Le premier est la description des colonnes du tableau
$null=$NodesException.MoveNext()

# $NodesException.Current.Value contient le texte complet: exception + msg d'erreur
While($NodesException.MoveNext())
{
#recherche indexé, ici on recherche le nom complet de l'exception
#Un noeud contient d'autres noeud
$Node=$NodesException.Current.Select($Recherche+\"[$i]/xhtml:td[1]/xhtml:«»span/mtps:InstrumentedLink\",$xmlnsManager)
$i++
#On place l'itérateur sur l'élément
$null=$Node.Movenext()
#<mtps:InstrumentedLink NavigateUrl=\"AssetId:T%3aSystem.ArgumentException\" runat=...
$null=$Node.current.MoveToAttribute(\"NavigateUrl\",\"\"«»)
#On crée un objet détaillant une exception
$Except=1|Select NameSpace,Nom,Texte
#Récupère le nom complet de l'exception,
#mais sans le \"AssetId:T%3a\" placé en début de chaîne.
#Puis on parse la chaine obtenue
$null=$Node.Current.Value.Substring($cAssetID.Length) -match \"(.*)\.(\w+)$\"
$Except.Nom=$Matches[2]
$Except.NameSpace=$Matches[1]
#On ne récupère que le texte en supprimant le nom
#de l'exception placée en début de chaîne
# ex: \"ObjectDisposedExceptionLe RegistryKey qui fait l'objet d'un acc?s ...\"
$Except.Texte= $NodesException.Current.Value -replace \"^$($Matches[2])\"
$Except.Psobject.TypeNames[0] = \"ExceptionDetail\"
#On mémorise l'exception
$Objet.Exceptions+=$Except
} #While $NodesExceptions
$Objet
}
[/code:1]

[code:1]
function Convert-ExceptionToCode($TabExceptions,[string] $Cible=$(throw \"Le langage cible ('PS', 'C#', 'Delphi') doit être renseigné.\"«»))
{
if ($TabExceptions.Psobject.TypeNames[0] -ne \"ExceptionInfos\"«»)
{throw \"ExceptionInfos n'est pas du type personnalisé [ExceptionInfos].\"}

switch ($Cible)
{
\"PS\" {
\" #$($TabExceptions.MemberName)\"
\"trap`r`n {\"
\"`t`$TypeName=`$_.Exception.GetType().FullName\"
\"`tswitch (`$TypeName)`r`n`t {\"
$TabExceptions.Exceptions|`
% {if ($_.Psobject.TypeNames[0] -ne \"ExceptionDetail\"«»)
{Write-Error \"Un object n'est pas du type personnalisé [ExceptionDetail].\"}
else
{
\"`t `t`\"$($_.NameSpace).$($_.Nom)`\" {throw `\"$($_.Texte)`\"}\"
}
}#If
\"`t `t`default { Write-Debug `\"Re Trown`\"; Throw new-object `$TypeName (`$_.Message)}\"
\"`r`n`t}`r`n }\"
\"# Insérez votre code \"
}#PS

\"C#\" {throw \"N'est pas implémenté.\"}
\"Delphi\" {throw \"N'est pas implémenté.\"}
default {throw \"Le langage cible ($Cible) est inconnu.\"}
}#Switch
}
[/code:1]
Pour le moment c'est un prototype opérationnel :)

Tutoriels PowerShell

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

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