[..] ObjEvent n'est pas declenche lors d'une erreur COM

Aide et conseils concernant AutoIt et ses outils.
Règles du forum
.
Répondre
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

[..] ObjEvent n'est pas declenche lors d'une erreur COM

#1

Message par xavierha »

Quand une requete ne retourne rien, RecordSet.MoveFirst ou MoveLast va generer une erreur.
Je travaille avec SQLCE (SQL Compact). Avec cette base de donnees, Recordset.RecordCount retourne toujours -1. Donc on ne sait pas que la table est vide a moins de pieger une erreur avec MoveFirst est essentiel dans le cas ou la requete ne retourne rien.
Dans le code ci dessous $oRS.MoveFirst fait culbutter AutoIT, l'erreur n'est pas piegee. Y a-t-il quelquechose qui m'echappe?

Code : Tout sélectionner

Global $oMoveFirstError
Global $g_eventerror = 0

Func RunCEQuery ( $Query, $DBFile, $bReturnColumns = False )
Dim $intNbLignes
Dim $Ret = ""
    Dim $sqlCon = ObjCreate("ADODB.Connection")
    $sqlCon.Open("Provider=Microsoft.SQLSERVER.CE.OLEDB.4.0;Data Source=" & $DBFile)
    Dim $oRS = ObjCreate ( "ADODB.Recordset" )
    $oRS.open ( $Query, $sqlCon, 3, -1)
    $intNbLignes = $oRS.recordCount
    if $intNbLignes > 0 or StringLen ( $DBFile ) > 0 Then
        $oMoveFirstError = ObjEvent ( "AutoIT.Error", "Errhandler" ) ; PIEGE A ERREUR
        $oRS.MoveFirst   ; ERREUR ICI si la requete ne retourne rien
        If $g_eventerror then
            $g_eventerror = 0
            Msgbox (0,"AutoItCOM test","Error was caught: " & @error)
        Endif
        Dim $iRec = 1
        if $bReturnColumns Then
            for $iField = 0 to $oRS.Fields.Count - 1
                if $iField = 0 Then
                    $Ret = $Ret & @CRLF & $oRS.Fields.Item(0).name
                Else
                    $Ret = $Ret & @TAB & $oRS.Fields.Item($iField).name
                EndIf
            Next
        EndIf
        While not $oRS.EOF
            for $iField = 0 to $oRS.Fields.Count - 1
                if $iField = 0 Then
                    $Ret = $Ret & @CRLF & $oRS.Fields.Item(0).value
                Else
                    $Ret = $Ret & @TAB & $oRS.Fields.Item($iField).value
                EndIf
            Next
            $oRS.MoveNext
        WEnd
        if StringLen ( $Ret ) > 2 Then $Ret = StringRight ( $Ret, StringLen ( $Ret ) - 2 )
    EndIf
    $oRS.close
    Return $Ret
EndFunc

;-------------------

Func Errhandler()
    Msgbox(0,"SQLQueryTool","L'erreur est dans le piege!")
    Local $err = $oMoveFirstError.number
    If $err = 0 Then $err = -1
    $g_eventerror = $err
EndFunc

 
La fonction Errhandler() n'est pas declenchee, et le script culbutte sur la ligne $oRS.MoveFirst si le recordset est vide, avec cette erreur:
==> The requested action with this object has failed.:
$oRS.MoveFirst
$oRS.MoveFirst^ ERROR

Je suis a cours d'idees, mais j'espere que quelquechose va vous sauter aux yeux...
merci a l'avance
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#2

Message par jchd »

Moi j'essaierais de placer l'enregistrement de la fonction d'erreur COM _avant_ toute création d'objet...
Parce que une fois l'objet instancié et s'il n'existe pas de "AutoIt.Error" handler déclaré, et bien la hiérarchie d'objets créés fait sans. En déclarer une après instanciation n'est apparemment pas prévu par Seattle et, entre-nous, ça me semble foncièrement logique.

Par ailleurs, il me semble que créer une connexion à chaque requête n'est pas forcément le plus efficace ni le plus logique.
L'ordre des choses voudrait plutôt :
1) enregistrement du gestionnaire d'erreurs COM
2) création de la session (connexion)
3) on fait le boulot dans cette session
4) fermeture de la connexion
5) libération (optionnelle) du gestionnaire d'erreurs COM
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#3

Message par xavierha »

Jchd, merci d'avoir pris le temps de comprendre le probleme et de faire une suggestion.
J'ai essaye me mettre la ligne

Code : Tout sélectionner

$oMoveFirstError = ObjEvent ( "AutoIT.Error", "Errhandler" )
juste au dessus de la ligne

Code : Tout sélectionner

Dim $oRS = ObjCreate ( "ADODB.Recordset" )
, mais ca ne marche pas non plus :( . La position de la ligne d'initialisation du handler d'erreur COM juste avant la ligne ou le script culbutte est la meme que dans l'exemple d'utilisation d'ObjEvent, qui lui-meme marche sans probleme (je l'ai essaye):

Code : Tout sélectionner

; Exemple de Handler d'erreur COM
; ------------------------- 

$oIE=ObjCreate("InternetExplorer.Application.1")    ; Creer une application Internet Explorer

Global $g_eventerror = 0    ; a verifier pour voir s'il y a eu une erreur. Doit etre remis a 0 apres usage.

$oMyError = ObjEvent("AutoIt.Error","MyErrFunc")    ; Initialisation d'un handler d'erreur COM

$oIE.UnknownMethod      ; Appel d'une methode qui n'existe pas

If $g_eventerror then
    $g_eventerror = 0
  Msgbox (0,"AutoItCOM test","succes de l'essai: erreur numer0: " & @error)
Else
  Msgbox (0,"AutoItCOM test","Echec de l'essai!")
Endif

Exit

; Le handler d'erreur
Func MyErrFunc()
  Msgbox(0,"AutoItCOM Test","Erreur COM !"      & @CRLF  & @CRLF & _
             "err.description : "    & @TAB & $oMyError.description    & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription & @CRLF & _
             "err.number : "         & @TAB & hex($oMyError.number,8)  & @CRLF & _
             "err.lastdllerror : "   & @TAB & $oMyError.lastdllerror   & @CRLF & _
             "err.scriptline : "     & @TAB & $oMyError.scriptline     & @CRLF & _
             "err.source : "         & @TAB & $oMyError.source         & @CRLF & _
             "err.helpfile : "       & @TAB & $oMyError.helpfile       & @CRLF & _
             "err.helpcontext : "    & @TAB & $oMyError.helpcontext _
            )
            
    Local $err = $oMyError.number
    If $err = 0 Then $err = -1
    
    $g_eventerror = $err  ; a verifier apres le retour de la fonction
Endfunc
 
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#4

Message par jchd »

Et si tu mets le handler avant la connexion ?
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#5

Message par xavierha »

j'ai essaye ca aussi, sans succes :(
GaRydelaMer
Niveau 7
Niveau 7
Messages : 450
Enregistré le : mer. 18 mars 2009 22:12
Localisation : Montpellier
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#6

Message par GaRydelaMer »

Bonjour

y a surement d'autres méthodes que vérifier l'erreur de ton objet.

EN ADO tu as 2 méthode du recordset, EOF, et BOF (End of file et begin of file) en gros quand tu as pas d'enregistrement dans ton recordset les 2 méthodes renvoie vrai.

De plus c'est fort utile dans les boucles pour parcourir le recordset

Code : Tout sélectionner

Global $n_MyError = 0, $o_MyError = ObjEvent("AutoIt.Error", "_MY_AU3_ErrFunc")
Func _MY_AU3_ErrFunc()
    Local $Msg = "We intercepted a COM Error !" & @CRLF & @CRLF & _
            "err.description is: " & @TAB & $o_MyError.description & @CRLF & _
            "err.windescription:" & @TAB & $o_MyError.windescription & @CRLF & _
            "err.number is: " & @TAB & Hex($o_MyError.number, 8) & @CRLF & _
            "err.lastdllerror is: " & @TAB & $o_MyError.lastdllerror & @CRLF & _
            "err.scriptline is: " & @TAB & $o_MyError.scriptline & @CRLF & _
            "err.source is: " & @TAB & $o_MyError.source & @CRLF & _
            "err.helpfile is: " & @TAB & $o_MyError.helpfile & @CRLF & _
            "err.helpcontext is: " & @TAB & $o_MyError.helpcontext

    ConsoleWrite(@ScriptName & " (" & $o_MyError.scriptline & ")" & @LF)
    ConsoleWrite($Msg & @LF)

    $n_MyError = Hex($o_MyError.number, 8)
    If MsgBox(4, "AutoItCOM Erreur Continuer ?", $Msg) <> 6 Then Exit
EndFunc   ;==>_MY_AU3_ErrFunc

;; Global $oConn = 0; Ici tu ouvres ta connection
Global $oRS = 0; Ici tu ouvres ton recorset

If $oRS.EOF Then
    Return SetError(1, 0, "No record")
EndIf
While Not $oRS.EOF
    ;;; La ligne courante de ton RecordSet, à l'ouverture du RecordSet tu es sur la 1ere ligne

    $oRs.MoveNext ; On se déplace de 1 ligne
Wend
Et pour trouver le nombre d’enregistrement de ton record:

"SELECT Count(*) FROM laTable" et avec un where pourquoi pas.
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#7

Message par xavierha »

Tu as raison, EOF et BOF permettent d'eviter la situation, et s'il y avait une icone qui se tappe sur la tete avec une chaussure, je la choisirais ici pour ne pas avoir pense a ca :D
Mais je pense qu'il y a une bogue avec ObjEvent, qui n'a pas l'air de fonctionner correctement avec certains objets ou methodes.

Un grand merci!
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#8

Message par jchd »

Il serait intéressant d'essayer avec la dernière bêta. trancexx nous a fait du bon boulot sur COM et les objets et elle sait de quoi elle parle. Tu devrais poster sur le forum US à ce sujet aussi, tu risques d'avoir des remontées sur SQL Compact, pas seulement sur ce point précis d'ailleurs.

Pour mon udf ADOsql, j'ai aussi employé .EOF dans certains cas. Plusieurs pilotes ADO se mélangent les crayons sur .RecordCount et j'en déconseille fermement l'emploi. Pourquoi ? Parce que le moteur ne peut connaître le nombre de rangées remontées par une requête générale qu'en exécutant en interne un .MoveNext itératif. Dans la majorité des cas, ta requête ne se borne pas à un SELECT count(...) et tu veux en lire le contenu, ce qui provoque un second balayage de la base. C'est ça ou bien le moteur stocke temporairement le recordset en mémoire ou sur disque, ce qui est tout autant un inutile gâchis de ressources.

Dans les posts, on voit souvent du code qui revient à la même double peine (2n lignes lues) :
► Afficher le texte
ou même (suprême horreur) à une peine quadratique (n² + 2n lignes lues):
► Afficher le texte
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#9

Message par xavierha »

Je me sers de la beta 17, qui n'est pas la derniere. ObjEvent echoue dans la version actuelle, 3.3.6.1, et dans la version beta 17. Je vais essayer avec la version beta 18.
Ma solution actuelle pour recuperer le nombre de rangees pour affichage est de ne le faire qu'apres coup.
Les donnees sont retournees sous un format de texte separe par des | et CRLF. Avec StringSplit, j'ai les lignes completes, et je les ajoute a un listview. A la fin, j'ai le nombre de rangees pret a afficher. De cette facon, il n'y a pas de ressources supplementaires utilisees.
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#10

Message par jchd »

Tu as peut-être plus simple et beaucoup plus efficace.
Adapte le code de _AdoSQL_GetData2D ci-dessous, ou fais un essai avec l'UDF avec juste un _ArrayDisplay et sans aucun code applicatif. Avec la plupart des moteurs ADO, la différence est flagrante.
_AdoSQL_GetData2D fait un .GetRows ce qui renvoit un tableau AutoIt 2D en un seul coup. Si tu as besoin des noms des colonnes, fais un _AdoSQL_FetchNames mais évite _AdoSQL_GetTable2D qui, pour la compatibilité avec son homologue SQLite, met les noms en rang 0 du tableau (ce qui oblige à recopier les données en les décalant d'une rangée).

Sur un recordset de 100 rangées, on ne voit pas de différence significative, mais sur des requêtes volumineuses ou répétitives, ça peut valoir le coup.
AdoSQL.au3
(21 Kio) Téléchargé 79 fois
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
xavierha
Niveau 3
Niveau 3
Messages : 31
Enregistré le : mer. 21 sept. 2011 23:08
Localisation : Atlanta, GA
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#11

Message par xavierha »

merci, je n'avais pas vu ce fichier #include. Pour ce programme, je peuple un ListView avec les donnees de la table, et je devrais reconvertir le tableau en string avec separateur pour generer les ListViewItem, ce qui annulerait l'avantage de vitesse. Mais j'ai un programme d'automation d'essais qui fait un usage intensif de bases de donnees, ou ca serait avantageux :D!
GaRydelaMer
Niveau 7
Niveau 7
Messages : 450
Enregistré le : mer. 18 mars 2009 22:12
Localisation : Montpellier
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#12

Message par GaRydelaMer »

Re,

voila une fonction qui te servira a peupler ta listview avec un tableau à 2 dimension:

Le hWnd de la listview
Par référence le tableau 2d
Un booléen pour ajouter (par défaut) ou remplacer le contenu de la listview
Un tableau avec le nom des colonnes
Un booléen pour effacer aussi les colonnes à utiliser avec le tableau des noms de colonnes
Un entier pour commencer à lire le tableau à la position de ton choix

La fonction principale utilisr: _GUICtrlListView_AddArray($hWnd, $ArrayItems) voir dans l'aide.

Code : Tout sélectionner

Func My_GUICtrlListView_AddArrayItems($hWnd, ByRef $ArrayItems, $DeleteItems = False, _
        $ArrayColumns = False, $DeleteColumns = False, $StartAt = 0)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    _GUICtrlListView_BeginUpdate($hWnd)
    If $DeleteItems Then
        _GUICtrlListView_DeleteAllItems($hWnd)
    EndIf
    If $DeleteColumns Then
        While _GUICtrlListView_DeleteColumn($hWnd, 0)
        WEnd
    EndIf
    If IsArray($ArrayColumns) Then
        For $i = 0 To UBound($ArrayColumns) - 1
            _GUICtrlListView_AddColumn($hWnd, $ArrayColumns[$i])
        Next
    EndIf
    If $StartAt > 0 Then
        Local $a = $ArrayItems
        For $i = 0 To $StartAt - 1
            _ArrayDelete($a, 0)
        Next
        _GUICtrlListView_AddArray($hWnd, $a)
    Else
        _GUICtrlListView_AddArray($hWnd, $ArrayItems)
    EndIf
    _GUICtrlListView_EndUpdate($hWnd)
EndFunc   ;==>My_GUICtrlListView_AddArrayItems
 
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#13

Message par jchd »

Quitte à utiliser du spécifique, autant peupler/compléter directement la listview en créant une version ad hoc de _AdoSQL_GetTable().
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
GaRydelaMer
Niveau 7
Niveau 7
Messages : 450
Enregistré le : mer. 18 mars 2009 22:12
Localisation : Montpellier
Status : Hors ligne

Re: [..] ObjEvent n'est pas declenche lors d'une erreur COM

#14

Message par GaRydelaMer »

J'ai aussi :P

Je vous ai mis les fonctions nécessaire:
My_GUICtrlListView_SetSQL
_ADORecordSet_Open
My_GUICtrlListView_AutoSizeColumns
► Afficher le texte
Je m'en sers tout le temps avec mes recordset ADO, en plus j'utilise le ItemParam de la listview pour y stocker la klé numérique (champ autoincrement) de la table. Ainssi dans les autres fonctions de gestion de la listview, style item click je retrouve la ligne dans la table en lisant juste
_GUICtrlListView_GetItemParam().

De plus suivant le type de champ j'aligne les données à gauche, au centre ou à droite.
Répondre