16

All I'm trying to do here is save a reference to the currently active window, but it doesn't seem to be working. It gives me a run time error on the last line.

Dim SourceWindow As Window, QACheckWindow As Window
SourceWindow = ActiveWindow

I'm not exactly sure why. Isn't ActiveWindow supposed to return the currently active window? If not, how can I make a reference to it?

EDIT: The above is right at the beginning of my function, so all there is before it is Sub FuncName()

supercheetah
  • 3,200
  • 4
  • 25
  • 38

1 Answers1

31

In VB object variables require the Set keyword to be assigned. Object properties that are objects also need to be Set. Runtime error 91 "object variable not set" is raised when the assignment doesn't use that keyword.

This is inherited from legacy Let keyword to assign values, and Set keyword to assign references; the Let eventually was deprecated (although still needed for defining properties) and the Set remained, leaving the VB6/VBA value assignment syntax like [Let] variable = value, where "Let" is optional.

In the declaration and assignment:

Dim SourceWindow As Window, QACheckWindow As Window
'this is like saying "Let SourceWindow = ActiveWindow":
SourceWindow = ActiveWindow

SourceWindow is an object, assigned as if it were a value - this causes VBA to attempt let-coercion through a default member call. If the object wasn't initialized, the member call fails with error 91. If the object was initialized but doesn't have a default member, error 438 is raised.

So in this case error 91 is being raised because of an implicit member call; the .net equivalent would be a NullReferenceException:

Dim SourceWindow As Window, Dim WindowTitle As String
'"SourceWindow" reference isn't set, the object can't be accessed yet:
WindowTitle = SourceWindow.Caption 

I'm going to go a bit overboard here, but the legacy Let statement should not be confused with the Let clause (in VB.net) which, in the LINQ query syntax (in VB.net), computes a value and assigns it to a new, query-scoped variable (example taken from MSDN):

From p In products 
Let Discount = p.UnitPrice*0.1 '"Discount" is only available within the query!
Where Discount >= 50
Select p.ProductName, p.UnitPrice, Discount

VB.net assigns both values and references, without the need to specify a Let or a Set, because in .net this distinction is a much thinner line, given how everything ultimately derives from System.Object... including System.ValueType. That's why the Set keyword was also deprecated in VB.net, and also why the VB.net syntax for defining properties has dropped the Let in favor of Set - because parameterless default members are illegal in VB.NET, so this ambiguous let-coercion doesn't happen.

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • Thanks. Coming from other languages, `Set` seems a little superfluously verbose. – supercheetah Sep 22 '13 at 17:03
  • 1
    Although pointing out the error in the code here, this answer is not fully correct. Error 91 is exactly the equivalent of a NullReferenceException. Whether you get error 91 or error 438 (member not supported) for a Let assignment on an object (without default member) depends on whether the object is initialized already. VBA will try to access a default member to get a value. If the variable is initialized and does not have a default member, you get error 438. If is is not initialized, you get error 91, because you tried to access a default member on Nothing. – M.Doerner Sep 15 '19 at 11:25