34
votes

VBA prend une mauvaise branche à IF-statement - Bogue du compilateur sévère?

Le point d'interrogation dans le titre n'est là que parce que je suis très réticent à appeler quoi que ce soit un bug du compilateur, mais dans ce cas, je serais surpris si quelqu'un pouvait expliquer ce comportement d'une autre manière.

Le code pour reproduire le problème est très, très simple. Dans un module standard, nous avons les éléments suivants:

Public Function CreateAndDestroyObject() As Long
    Dim oClass2 As cClass
    Set oClass2 = New cClass
    Set oClass2 = Nothing
End Function

Private Sub Class_Terminate()
End Sub

et nous avons une classe ( cclass ) définie dans un module de classe nommé CClass, contenant les éléments suivants Code:

Sub CompilerBug()
    Dim oClass As cClass
    Set oClass = New cClass

    If Falsee(oClass.CreateAndDestroyObject) Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If
End Sub

Public Function Falsee(lng As Variant) As Boolean
    Falsee = False
End Function

Le code est assez explicite. L'instruction si est entrée malgré la fonction bien nommée false renvoyant false , peu importe l'entrée! Le même résultat peut être observé lorsque la fonction de la classe est remplacée par une propriété publique similaire GET .

Aux fins de la reproduction, j'obtiens ce comportement dans mon bureau 365 Excel, 64bit, version 2011 (build 13426.20274) qui est la version actuelle à jour d'Excel. J'ai également testé ce code exact dans mon mot vba ide avec exactement les mêmes résultats.

"preuve":

" prepwer "> </a> </p> <p> Je n

si nous réécrivons le code dans notre sous:

Public Function Falsee(oClass As cClass) As Boolean
    Set poClass = oClass
    Falsee = False
End Function

(La première instruction si est omise par souci de concision.) Le code est exécuté comme prévu, il est donc crucial que la fonction soit appelée directement dans la condition pour le si Em> instruction (pas quelque chose qui devrait généralement faire une différence).

Et le prochain indice intéressant est le suivant (Supposons que nous utilisons à nouveau le sous-code buggy avec si false (oclass.clone) puis ): Si nous supprimons ce qui suit de notre module de classe:

Private Sub Class_Terminate()
End Sub

L'instruction Si fonctionne comme prévu et rien n'est imprimé! Donc, en quelque sorte, l'événement de résiliation en cours d'exécution lors de l'évaluation de l'IF-Statement gâche les choses, mais le sous-marin class_termiate () ne contient même aucun code! C'est la prochaine chose qui ne devrait pas faire de différence, mais le fait!

Cette idée est en outre soutenue par le fait, que lorsque nous déclarons une variable publique dans le module en ajoutant Public Poclass comme CClass en haut et réécrivez le code de fonction à:

Sub CompilerBug()
    Dim oClass As cClass
    Set oClass = New cClass

    Dim bFalse As Boolean
    bFalse = Falsee(oClass.Clone)

    If bFalse Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If
End Sub

Maintenant, l'événement de terminaison ne se fait pas appelé lors de l'exécution de la mise en place, car l'instance de La classe ne sort pas de portée lors de l'exécution de la staté IF et, par conséquent, la statement IF évalue correctement - la ligne ne s'imprime pas.

De toute évidence, le terminaison Être exécuté lors de l'évaluation de la déclaration IF ne peut pas être toute l'histoire, car cela se produit tout le temps. Cela semble également avoir quelque chose à voir avec la portée de l'objet qui est terminé et la façon dont le paramètre est transmis à la fonction. Par exemple, les éléments suivants pas produisent le même comportement:

Code du module:

Public Function Clone() As cClass
    Dim oClass As cClass
    Set oClass = New cClass
    Set Clone = oClass
End Function

Private Sub Class_Terminate()
End Sub

Et dans le module de classe:

Sub CompilerBug()
    Dim oClass As cClass
    Set oClass = New cClass

    If False Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If

    If Falsee(oClass.Clone) Then
        Debug.Print "This does print, although it shouldn't!"
    End If
End Sub

Public Function Falsee(oClass As cClass) As Boolean
    Falsee = False
End Function

Pour tout résumer, le comportement se produit lorsque:

une méthode d'une classe renvoie une instance de la même chose classe, et cette méthode est appelée à l'intérieur de l'état d'une stades IF, comme argument pour une fonction, et cette instance de la classe (qui a été créée par la méthode) sort ensuite de la portée à l'intérieur de cette fonction et de l'événement terminant de La classe est appelée (et existe comme code). Dans ce cas, l'instruction IF est entrée, quelle que dans ce cas? L'un de mon code est-il censé produire un comportement non défini ou est-ce en fait un bug? Y a-t-il d'autres cas où les stades si ne fonctionnent pas de la manière attendue? Qu'est-ce qui cause exactement ce bug?

Le problème ne semble pas exister dans la version 32 bits d'Excel.


8 commentaires

Dans Excel O365 32 bits, je ne suis pas en mesure de reproduire votre problème. Rien n'imprime, et en ligne de ligne, chaque ligne s'exécute.


@RonRastfeld Je l'ai également essayé sur la version 32 bits sur un autre PC, je ne peux pas non plus le reproduire là-bas. Je viens également de demander à un collègue qui dirige également la version 64 bits, il a pu le reproduire.


Très étrange. N'aurait pas pensé que la "bitness" ferait une différence sur quelque chose comme ça.


Pour de nombreuses raisons, je ne fais toujours pas confiance à Excel 64 bits. Je ne le recommande jamais.


Pas de compétence VBA, mais cela pourrait-il avoir quelque chose à voir avec le «faux» mal orthographié comme «faux»?


@Curiousrabbit no. J'ai également testé le code avec d'autres noms de fonction. En fait, dans le projet sur lequel je travaillais lorsque j'ai trouvé le bug, cela s'est produit avec plusieurs fonctions différentes.


Peut-être que je n'ai tout simplement pas pris assez de café un lundi matin, mais n'est-ce pas simplement un malentendu de la façon dont " si function () {} " évalue? Vous lui demandez d'évaluer une fonction ici, et la fonction s'exécute correctement, donc elle imprime la boîte de dialogue que vous avez écrite. Il ne vérifie pas si False évalue à false , seulement s'il évalue avec succès . Vous pouvez y mettre une affectation variable aléatoire qui n'a rien à voir avec les vraies booléens et les faux et cela vous donnerait la même sortie.


@Tylerh non. La façon dont les stades IF VBA sont censées fonctionner est la suivante: Si la condition évalue à true , l'instruction est entrée. Si la condition évalue à false L'instruction passe au suivant elseif , else ou se terminer si , selon la première éventualité . (Bien sûr, ignorer les stades if imbriquées entre si false alors et la partie suivante elseif ...) Cela n'a rien à voir avec la fonction évaluant «avec succès». Si la fonction ne réussit pas, le programme lancera une erreur si aucune gestion d'erreur n'est en place.


2 Réponses :


15
votes

Ce bug n'est pas présent sur 32 bits, mais il semble être présent dans des applications compatibles VBA 64 bits (j'ai essayé Excel, Word et autocad ).

Puisque la question couvre déjà ce qui se passe si l'objet ne se termine pas ou s'il n'y a pas class_termine Événement, les exemples suivants utilisent tous un objet qui va sûrement sortir de la portée et nous supposons également qu'il existe un class_termine qui est appelé:

Option Explicit

#If Win64 Then
Sub Bug()
    ' We don't really need a Clone method to reproduce the bug
    If Falsee(New cClass) Then
        Debug.Print "This does print, although it shouldn't!"
    End If

    ' If we add a logical operator and a second method call then the bug disappears:
    If Falsee(New cClass) Or Falsee(New cClass) Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If

    ' It could be any other method. The order of the methods also doesn't matter
    If Falsee(New cClass) Or Sin(0) Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If

    ' The above workaround does not work if we simply use a boolean value after the method call
    If Falsee(New cClass) Or False Then
        Debug.Print "This does print, although it shouldn't!"
    End If

    ' But it does work if we add the boolean before the method call:
    If False Or Falsee(New cClass) Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If
    If True And Falsee(New cClass) Then
        Debug.Print "This doesn't print, as it shouldn't."
    End If
End Sub

Function Falsee(oClass As cClass) As Boolean
    Falsee = False
End Function

#End If


2 commentaires

Intéressant, cela aggrave encore les choses! De plus, si vous laissez la fonction renvoyer autre chose, par exemple une chaîne, puis l'utilisez dans une comparaison, le bug apparaîtra: à l'intérieur de la fonction: falsee = "false" et dans le if- Instruction: if falsee (new CClass) = "true" alors -> si la déclaration sera entrée.


Remarque: Bien que les non-solutions ne soient pas généralement acceptés comme réponses, des réponses partielles qui fournissent des informations supplémentaires ou plus d'isoler ou de définir un problème, comme cela, sont les bienvenus si un La question n'a pas encore été résolue autrement: "Aidez-nous à trouver une solution en recherchant le problème, puis contribuez les résultats de vos recherches et tout ce que vous avez essayé en tant que réponse partielle. De cette façon, même si nous ne pouvons pas le comprendre , la prochaine personne a plus à continuer. " ( source )



3
votes

C'est en effet un bug de la version 64 bits de VBA7. Voici un exemple plus petit:

Someclass.cls:

False
True

main.bas:

False
False

Ceci imprime p>

Function ReturnFalse(o As Object) As Boolean
 ReturnFalse = False
End Function
Sub test()    
 Debug.Print ReturnFalse(New SomeClass)
 If ReturnFalse(New SomeClass) Then
  Debug.Print "True"
 Else
  Debug.Print "False"
 End If     
End Sub

sur VBA 32 bits et

Private Sub Class_Terminate()
End Sub

sur VBA 64 bits

Je pouvais trouver un Rapport sur cette erreur dans un poste UserVoice de presque trois ans ( https://excel.uservoice.com/forums/304921-excel-for-windows-desktop-application/suggestions/35735881 -Member-Calls-on-User-Objects-Bi ), mais il n'y a pas eu de réaction depuis lors. De plus, il n'y a apparemment pas de moyen "réel" accessible par l'utilisateur de signaler les bogues VBA à Microsoft. Tout cela est très frustrant, car nous avons quelques projets VBA qui doivent être en train de porter à 64 bits en raison de leurs exigences de mémoire.


0 commentaires