programing

VBA가 If-statement에서 잘못된 분기를 사용 - 심각한 컴파일러 버그?

i4 2023. 4. 21. 20:13
반응형

VBA가 If-statement에서 잘못된 분기를 사용 - 심각한 컴파일러 버그?

제목에 물음표가 있는 것은 컴파일러 버그라고 부르기 매우 어렵기 때문입니다만, 이 경우, 이 동작을 다른 방법으로 설명할 수 있는 사람이 있으면 놀라겠습니다.

이 문제를 재현하는 코드는 매우 간단합니다.표준 모듈에는 다음이 있습니다.

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

그리고 우리는 수업이 있다.cClasscClass라는 이름의 클래스모듈에 정의되어 있습니다.다음 코드를 포함합니다.

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

Private Sub Class_Terminate()
End Sub

그 암호는 꽤 자명하다. 번째 if 문은 적절한 이름의 함수에도 불구하고 입력됩니다.Falsee돌아오는False입력에 관계없이!클래스의 기능이 유사한 기능으로 대체된 경우에도 동일한 결과가 관찰될 수 있습니다.Public Property Get.

재생을 위해 최신 버전의 Excel인 Office 365 Excel, 64비트 버전 2011(Build 13426.20274)에서 이 동작을 볼 수 있습니다.또, 이 코드를 Word VBA IDE로 테스트했는데, 같은 결과가 나왔습니다.

'실증' :

'증명'

이 동작의 원인은 알 수 없지만, 여기 몇 가지 힌트가 있습니다.

서브의 코드를 다음과 같이 고쳐 쓴 경우:

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

( 번째 if 문장은 간결하게 하기 위해 생략됩니다.)코드는 예상대로 실행되므로 함수를 if 문의 조건으로 직접 호출하는 것이 중요합니다(보통 차이를 만드는 것이 아님).

그리고 다음 흥미로운 단서는 다음과 같습니다(버그 서브 코드를 다시 사용한다고 가정합니다).If Falsee(oClass.Clone) Then): 클래스 모듈에서 다음 항목을 삭제한 경우:

Private Sub Class_Terminate()
End Sub

if 문은 예상대로 작동하며 아무것도 인쇄되지 않습니다!그래서 왠지 If-statement 평가 중에 Terminate 이벤트가 실행되면 일이 엉망이 되지만Class_Terminate()잠수함은 코드도 포함하고 있지 않아!그 다음으로는 달라지지 않을 것입니다만, 실제로도 달라지고 있습니다!

이 아이디어는 모듈 내에 퍼블릭 변수를 선언할 때 다음과 같이 추가함으로써 더욱 뒷받침됩니다.Public poClass As cClass를 눌러 함수 코드를 다음과 같이 다시 작성합니다.

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

이제 If-statement 실행 중에는 클래스 인스턴스가 범위를 벗어나지 않고 결과적으로 If-statement가 올바르게 평가되므로 If-statement 실행 중에는 Terminate 이벤트가 호출되지 않습니다.

If-statement 평가 중에 실행되는 Terminate-event가 전부가 될 수 없는 것은 분명합니다.이는 항상 발생하기 때문입니다.또한 종료되는 객체의 범위와 파라미터가 함수에 전달되는 방식과도 관련이 있는 것으로 보입니다.예를 들어, 다음과 같은 동작은 발생하지 않습니다.

모듈 코드:

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

클래스 모듈에서는 다음과 같습니다.

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

Private Sub Class_Terminate()
End Sub

요약하면, 이 동작은 다음과 같은 경우에 발생합니다.

클래스의 메서드는 같은 클래스의 인스턴스를 반환합니다.이 메서드는 함수의 인수로서 If-statement 조건 내에서 호출됩니다.이 클래스의 인스턴스(메서드에 의해 작성된 것)는 그 함수 내에서 범위를 벗어나 클래스의 종료 이벤트가 호출되고(코드로 존재합니다).이 경우 함수의 반환값에 관계없이 if 문이 입력됩니다.

제게는 많은 의문들이 남아있습니다...이 경우 Terminate 이벤트가 영향을 미치는 이유는 무엇입니까?코드 중 정의되지 않은 동작을 발생시키는 것이 있습니까, 아니면 실제로 버그입니까?If-statement가 예상대로 작동하지 않는 다른 사례가 있습니까?이 버그의 원인은 무엇입니까?

32비트 버전의 Excel에서는 문제가 발생하지 않는 것 같습니다.

주의:

이 게시물에 기술된 버그는 2019년 11월에 수정되었습니다.이 버그의 변형이 아직 존재합니다.자세한 내용은 이쪽을 참조해 주십시오.

이 버그는 32비트에서는 발생하지 않지만 64비트 VBA 지원 애플리케이션(Excel, Word AutoCAD 사용)에서 나타나는 것 같습니다.

질문에서는 이미 되지 않거나 오브젝트가 하지 않는 어떤 있습니다.Class_Terminate에서는 모두 하고 있습니다. 이 오브젝트에는 반드시 범위를 벗어나는 오브젝트가 합니다.또한 이 오브젝트에는Class_Terminate뭇매를 맞다

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

64비트 버전의 VBA7의 버그입니다.다음으로 작은 예를 제시하겠습니다.

SomeClass.cls:

Private Sub Class_Terminate()
End Sub

Main.bas:

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

이 인쇄물은

False
False

32비트 VBA 및

False
True

64비트 VBA로

이 에러에 관한 리포트는, 3년 가까이 된 유저 보이스 투고(https://excel.uservoice.com/forums/304921-excel-for-windows-desktop-application/suggestions/35735881-fix-inlined-member-calls-on-user-objects-on-64-bi),)에서 확인할 수 있었습니다만, 그 후로는 반응이 없었습니다.또, VBA 버그를 Microsoft 에 보고하는, 유저가 액세스 할 수 있는 「실제」방법은 없는 것이 분명합니다.메모리 요구 사항 때문에 64비트로의 포팅이 절실한 VBA 프로젝트가 몇 개 있기 때문에 이 모든 것이 매우 어렵습니다.

언급URL : https://stackoverflow.com/questions/65041832/vba-takes-wrong-branch-at-if-statement-severe-compiler-bug

반응형