2

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

JMP
  • 4,417
  • 17
  • 30
  • 41
mwolfe02
  • 23,787
  • 9
  • 91
  • 161
  • I'm surprised to learn that `VBComponent.Saved` doesn't crash. The propery has no logical right for existing since it's obvious that you can't save any part of the whole, such as a worksheet. The `Saved` property is pertinent only to the entire project. Perhaps something like VBComponent.Parent.Saved works as you expect. – Variatus Sep 19 '20 at 00:50
  • According to the documentation, that property returns a Boolean value indicating whether **the object was *edited* since the last time it was saved** and **True** means that the object has **not been edited** since the last time it was saved. I'm not sure how meaningful it is for a **Project**. – Ron Rosenfeld Sep 19 '20 at 01:25
  • @Ron Rosenfeld It's meaningful for a project. It's meaningless for a component thereof. I failed to find MSDN's definition of the `VBComponent` object where I was hoping to find if it even has a `Saved` property, since it obviously shouldn't. If you found it, could you post a link? It seems to me that your explanation just refers to the `Saved` property in general. – Variatus Sep 19 '20 at 07:03
  • 1
    This might help - for a module `Saved` behaves as expected, for a [Type 100](https://learn.microsoft.com/en-us/office/vba/Language/Reference/Visual-Basic-Add-in-Model/properties-visual-basic-add-in-model#type), 'True' is `1` and 'False` is `-2`, but neither equal the boolean `True` or `False`: `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; IsSaved; Not IsSaved; IsSaved = True; IsSaved = False Next VBComp` – JMP Sep 19 '20 at 07:52
  • 3
    [THIS](http://vb.mvps.org/tips/truth/) explains it. See the section which talks about **First, understand that logical operations and tests are two different things.**. `Dim IsSaved As Boolean: IsSaved = 1` is a classic example. While `Debug.Print IsSaved` and `Debug.Print True = -1` and `Debug.Print IsSaved= -1` will give you `True` but `Debug.Print IsSaved = 1` will give you `False` – Siddharth Rout Sep 19 '20 at 09:14
  • @JMP module behaves the same as document-class (type 100) for me (office 2013 x64), but wtf makes `Not True` eval to `-2`. Its defined as `Boolean` in object browser docs and locals/watches? – ComputerVersteher Sep 19 '20 at 09:15
  • @SiddharthRout that's it! Do you know why they implemented it that way (with wrong docs)? – ComputerVersteher Sep 19 '20 at 09:23
  • @ComputerVersteher; I imagine 'saved' was one of the first properties Microsoft wrote into VBE way back, and `true` equalled 1 in those days :( – JMP Sep 19 '20 at 10:58
  • @Variatus I got that definition from [Properties (Visual Basic Add-In Model)](https://learn.microsoft.com/en-us/office/vba/language/reference/visual-basic-add-in-model/properties-visual-basic-add-in-model). But it doesn't explain why `True` = `1`. And it is also interesting that you can properly test the property using `Select Case: Case True: doX: Case False: doY:End Select` (at least I could do that here). – Ron Rosenfeld Sep 19 '20 at 11:29
  • @SiddharthRout: Thanks for the link. I wish the VBIDE library listed the `VBComponent.Saved` property as an `Integer` instead of `Boolean`. I think if a library declares a property to be a Boolean, then it should only set the value of said property to 0 or -1. At least I'll know now for next time. – mwolfe02 Sep 19 '20 at 22:06
  • @Ron Rosenfeld Thanks for the link. Do we know that the Add-in Model is relevant to the VBComponent object? We do know that `Saved` ***can't*** have the promised meaning because a component can't be saved. Therefore we can go hunting for an effective other use of the property, which must be unintended (misnamed) by definition and therefore undocumented and almost certainly unverifiable, or we can accept that the mere presence of the property is a bug, likely with some legacy reason. So long as I don't know its use or purpose I shouldn't care what it returns. – Variatus Sep 20 '20 at 00:32
  • @Variatus Not sure. As a VBComponent, I know empirically, for example (*using `Case` as I mention above*), that `ThisWorkbook` and `Worksheets` seem to return `True`, but `Modules` seem to return False. But I haven't really explored it. – Ron Rosenfeld Sep 20 '20 at 00:38
  • @Ron Rosenfeld That seems to underscore my point. Since a worksheet can't be saved independent from its workbook the question isn't why tits `Saved` property returns True or False but why there is such a property in the first place. Only after we have an answer to that we can wonder whether its a Boolean or a Long or a Variant and why it doesn't conform with what secretive MS generously lets us know about their general idea of the meaning of "Saved". – Variatus Sep 20 '20 at 00:45
  • @Variatus the docs explain that `.Saved` indicates whether the object was edited since the last time it was saved. – ComputerVersteher Sep 20 '20 at 00:58
  • @ComputerVersteher That's for the Add-in Model. Nobody said that the VBComponent is built on the Add-in Model. In fact, we don't know what, if anything other than Add-ins (please define), is built on the Add-in Model. How do you deal with the fact that it's not possible to save a component (e.g. a worksheet)? – Variatus Sep 20 '20 at 01:09
  • @Variatus I think we can be sure as first if you change code, property changes to false (no matter if standard-module class-module or document-module). Second help (`F1`) on `VBComponents` leads to the same doc. Of course I lack knowledge on Add-In Model, but I think it's named like that as usually add-ins use it. I will ask the gurus on RubberduckVBA, for sure they can explain;) – ComputerVersteher Sep 20 '20 at 01:19
  • @Variatus: Individual components can't be saved in Excel, but they can be saved *independently* in Access. That's actually the development environment I was most interested in, but I could reproduce the "bug" in Excel and I wanted to get more exposure for my question. – mwolfe02 Sep 20 '20 at 03:12
  • The fact that you can't save individual VB components in Excel seems to be a design decision in Excel's implementation of VBA. In Access, you can say `DoCmd.Save acModule, "Module1"` and only Module1 will be saved (other "dirty" modules would remain unsaved). – mwolfe02 Sep 20 '20 at 03:16
  • @mwolfe02 Thank you. Wouldn't that fact put paid to the applicability of the Add-in Model to this particular property and its object? – Variatus Sep 20 '20 at 04:01
  • @Variatus: I'm not sure what you're asking. – mwolfe02 Sep 20 '20 at 04:22
  • I'm suggesting that the Add-in Model, by which `Saved` holds a boolean value, doesn't apply. Therefore it's not necessarily a bug if `Saved` doesn't hold a boolean value. – Variatus Sep 20 '20 at 07:34
  • Except that `Saved` does hold a boolean value that conveys meaning in Excel, too. It's true that you can't save individual code modules in Excel the way you can in Access. But, say you save an Excel project with multiple modules. All modules are considered `Saved` immediately after you save the project. You then change one of those modules. Now, only that module returns `Saved = False` if you iterate through them again. My only point is that `Saved` should be shown as an `Integer` with documentation stating that it returns one of two values: 1 for True and 0 for False. – mwolfe02 Sep 22 '20 at 15:16

1 Answers1

2

@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?

ComputerVersteher
  • 2,638
  • 1
  • 10
  • 20
  • 1
    I think the reason will be too complicated for me to grasp. What I would like to see is documentation where the values this particular property can hold are detailed. MSDN usually has very good documentation for objects, where methods and properties are listed and where one can drill down to the values of properties. I didn't find such documentation for the VBComponent object and only that led me to look for enlightenment about the `Saved` property in what turned out to be the wrong place. I upvoted your answer and will downvote MS's lack of one if someone will tell me where I can do that. – Variatus Sep 20 '20 at 04:12
  • 1
    @Variatus you know https://office365.uservoice.com/? – ComputerVersteher Sep 20 '20 at 07:07