VBA Visual Basic Editor (VBE) object .Saved Boolean bug?

Viewed 241

tl;dr: In the code below, the following two conditions both evaluate to True!!! How? Why?

  • If Not IsSaved Then
  • If IsSaved Then

I'm working with the VBA Visual Basic Editor (VBE) object. The .Saved property has me baffled.

Both (.Saved) and Not (.Saved) return True if the VBComponent object is in fact saved. I even tried explicitly coercing the property to a Boolean before evaluating the conditional. Here are the steps to reproduce:

  1. Open a blank workbook in a new instance of Excel
  2. Copy and run the following code from a standard module (e.g., "Module1"):
Sub ListModules()
    Dim VBComp As Object  'VBComponent
    For Each VBComp In Application.VBE.ActiveVBProject.VBComponents
        
        Dim IsSaved As Boolean
        IsSaved = CBool(VBComp.Saved)
        
        If CStr(VBComp.Saved) = "True" Then
            Debug.Print vbNewLine; VBComp.Name; " saved? "; VBComp.Saved; " ("; TypeName(VBComp.Saved); ")"
            If Not IsSaved Then
                Debug.Print VBComp.Name; " is not saved"
            End If
            If IsSaved Then
                Debug.Print VBComp.Name; " is saved"
            End If
        End If
    Next VBComp
End Sub

'Sample output:
ListModules

ThisWorkbook saved? True (Boolean)
ThisWorkbook is not saved
ThisWorkbook is saved

Sheet1 saved? True (Boolean)
Sheet1 is not saved
Sheet1 is saved

NOTE: To run this code, you may have to grant access to the VBA project object model: File -> Options -> Trust Center -> [Trust Center Settings...] -> Macro Settings -> [x] Trust access to the VBA project object model

1 Answers

@JMP and @SiddharthRout comments on question revealed the answer.

It's a non standard boolean implementation, where Trueis 1not -1.

But with that, logical operations fail.

E.g a Not A operation is the bitwise inversion of A (booleans are stored as long). If A=-1 its bits are 1111111111111111 and the inverson is 0000000000000000.

But if A=1 then its bits are 0000000000000001 and the inversion gets 1111111111111110 what is representing -2. So Not 1 is -2 what is True as it is <> 0!

That's why Not VBComponent.Saved = True (=-2) when VBComponent.Saved = True (=1) too!

| Assignment (a As Boolean) | Bits             | Value As Integer | Value As Boolean |
|---------------------------|------------------|------------------|------------------|
| a = True                  | 1111111111111111 | -1               | TRUE             |
| Not a                     | 0000000000000000 | 0                | FALSE            |
|                           |                  |                  |                  |
| a = False                 | 0000000000000000 | 0                | FALSE            |
| Not a                     | 1111111111111111 | -1               | TRUE             |
|                           |                  |                  |                  |
| VBComponent.Saved = True  | 0000000000000001 | 1                | TRUE             |
| Not VBComponent.Saved     | 1111111111111110 | -2               | TRUE             |
|                           |                  |                  |                  |
| VBComponent.Saved = False | 0000000000000000 | 0                | FALSE            |
| Not VBComponent.Saved     | 1111111111111111 | -1               | TRUE             |
Sub TestInversion()
Dim VBComp As Object  'VBComponent
Debug.Print
For Each VBComp In Application.VBE.ActiveVBProject.VBComponents
IsSaved = CInt(VBComp.Saved)
Debug.Print VBComp.Name; VBComp.Type; VBComp.Saved; CInt(VBComp.Saved); Not VBComp.Saved; Not CInt(VBComp.Saved); VBComp.Saved = True; VBComp.Saved = False
Next VBComp
End Sub

Now we are missing a reason for that strange implementation, maybe someone knows?

Related