What is the exact difference between the vba operators ! and . ?
I have noticed that often they can be used interchangable, but sometimes one works and one doesn't (Forms-Collection in Access e.g.)
Aswell, when you accidentially write !optSomething (some optionbuttion) instead of Not optSomething withing a with clause, it will not break at compile but at runtime, because ! is used - my company had some broken vba after On Error Resume Next with this, and i only realised when having "brake on every error" activated.
The ! character invisibly calls the default member for that object.
For a Recordset, the default member is the Fields method, and the default member of a field object is the Value property, so these lines are identical:
sEmp = rst.Fields("emp_id")
sEmp = rst.Fields("emp_id").Value
sEmp = rst!emp_id
So, the reason you'll see it work for some objects and not others, is probably because those objects don't have the default member that you expect.
Related
In my current project in Access VBA, I created a window which works like a console and now I am trying to add a possibility to display any public variable. It should work like:
Show_var [variable_name]
So it should be like in the direct window where i can type:
? pVar.variable_A
I found out that using
Application.VBE.ActiveVBProject.VBComponents(11).CodeModule.CountOfLines
I can display the number of lines within a module or form so I thought perhaps I could somehow find the variables there, cycle through them and when the correct one is found, its value can be shown. OFC I could make a Select Case Statement where all variables are included but that is not the way I want to do it because it is complicated and must be changed every time update my public variable list.
There are so many problems that I think you are out of luck. Let me just list some of them:
There is no way to get a list of all variables - that would mean you would need access to the internal symbol table.
As a consequence, you would have to read the code (CodeModule lets you read the code of a module), and write an own parser to fetch all declarations (hopefully you use Option Explicit)
AFAIK, the is no method that can access the content of a variable via it's name.
Even if there would be any such method: What would you do with different data types, arrays and especially with objects.
If a user runs into problems, often it is a runtime error. If you don't handle that errors with some kind of error handler, the only option if a user cannot enter the debugger is to press "End" - which would destroy the content of all variables.
You have to deal with scope of variables, you can have several variables with the same name and you will likely have lots of variables that are out of scope in the moment you want to dump them.
I'm not successful in using the Bang (!) operator without it's argument being hardcoded, i.e., Me.VBProject.References!Excel. In this example, the Excel reference is hardcoded.
Out of frustration I've tried all permutations I can think of in an attempt to utilize it:
[Me.VBProject.References!(str)]
[Me.VBProject.References! & (str)]
["Me.VBProject.References!" & str]
["Me.VBProject.References!" & (str)]
and many more with parens added to ensure proper pre-evaluation including the longhand Application.evaluate method. Nada!
No, you can't do that.
The bang operator is just a shortcut for calling the default member of the object, and passing the text after the bang as a string to the first parameter of the default member:
The bang notation:
Me.VBProject.References!Excel
is exactly equivalent to:
Me.VBProject.References.Item("Excel")
and, because it is the default member, you can omit the Item function call:
Me.VBProject.References("Excel")
So, to use your (really badly named) variable str:
str = "Excel"
Debug.Print Me.VBProject.References.Item(str).Name
This is an X-Y problem.
Bang notation is a means to an end.
It's one of the tools made available to you, to retrieve an item from a collection.
Nothing more, nothing less.
What you want isn't to use the bang operator with a variable.
What you want is to retrieve an item from a collection using a variable.
Collection types have a default member, typically named Item. Default members can be specified explicitly, or implicitly accessed:
Dim foo As New Collection
foo.Add Item:=42, Key:="test"
Debug.Print foo.Item("test") 'explicit reference to the default member
Debug.Print foo("test") 'implicit reference to the same default member
The bang operator is just another way to make an implicit call to the collection's default member:
Debug.Print foo!test
All 3 Debug.Print statements above will call the foo.Item default member to output the 42 associated with the key "test".
Square brackets
As you can see, what comes immediately after the ! operator is really a string literal. Because a string literal can contain whitespace, the VB6/VBA parser needed a way to support them.
That's what the [square brackets] are for:
foo.Add 72, "spaces in the key"
Debug.Print foo![spaces in the key]
When they're not delimiting a string literal for bang notation, square brackets are usually1 interpreted as a run-time expression for the host application to evaluate. For example this is legal (though questionably advisable) in Excel VBA:
Debug.Print [A1]
The VBA parser identifies a bracketed expression and defers its evaluation to the host application - here Excel; at run-time, the instruction ultimately equates to:
Debug.Print ActiveSheet.Range("A1").Value
If you don't believe the evaluation of a bracketed expression is deferred to the host application, consider what needs to happen for this instruction to print 4:
Debug.Print [Sum(2,2)]
Therefore, every single one of the attempts/permutations in your post missed the mark and made Excel try to evaluate an expression that only VBA would be able to resolve, because Me.VBProject.References means absolutely nothing to Excel.
Square-bracket expressions should usually be avoided, because you lose compile-time checks and any error can only be caught at run-time.
1Usually, because they can also be used in some identifiers,
for example in Enum types, to make a [_hidden] enum member.
Bottom Line
Bang notation is a way to retrieve an item from a collection by leveraging default members and making string literals look like an identifier. You can't make it work without "hard-coding" the string literal, because it requires a string literal.
If you want to parameterize the collection retrieval, you can't use the bang operator.
It's useful for typing the code faster. If you don't know exactly how it works and what it does for you though, it's a double-edged blade that hides what's really going on and ultimately makes the code harder to read and understand. Code shouldn't be written just to be run. Code should be written to be read and understood.
Note: Bang notation isn't actually only for collections. It actually passes its argument as a string literal to the first parameter of anything that has a default member. I would strongly advise avoiding it for anything other than a collection class though (e.g. Collection.Item, Workbooks.Item, Worksheets.Item, Recordset.Fields, etc.), for the sake of future maintainers' sanity.
When writing the following VBA, what is the root cause of the error "Expected =" given that we are using the Format:=2.
Workbook.Open (filename, Format:=2)
I understand that this format works when setting the variable as in the following code, but why does it work here and not in the above format?
Set wrkb = Workbook.Open (filename, Format:=2)
Also what is this operator called, := and how is it used?
It's not an operator, it's a named argument.
You can chose the order of the arguments/parameters by directly specifying what they are.
The concept of named arguments also exists in multiple modern languages, such as c# (its optional, just like in VBA) and swift (by default it's required, but you can disable it).
Named arguments also allow you to omit arguments that are optional altogether, but pass an argument that is further back in the list of arguments. A good way to try named arguments out is the messagebox, since it has many optional arguments with default values.
Example: MsgBox only specifying title:
MsgBox Title:="wew lad"
Or, in the more modern way of writing vb(a) code:
Call MsgBox(Title:="wew lad")
The MsgBox is a good example, since you can call it normally, and then specify a parameter further back directly (works with other methods too):
Call MsgBox("Yes!", Title:="wew lad")
Once there are named parameters, you can't add ordered parameters after:
'Call MsgBox (Prompt:="Yes!", vbOkCancel Title:="wew lad")
'this doesnt work!
Now, why does this raise an error:
MsgBox ("Hello", Title:="test")
This is some of the weirder parts of vba. Calling functions with return values but ignoring the value while using parentheses is a bit broken.
You can circumvent this by adding a Call in front of it (vba then knows it can ignore the result).
This would look like this:
Call MsgBox ("Hello", Title:="test")
Honestly, I don't know why this is, but I've had it cause really weird bugs. When you use the parentheses syntax, I can really recommend using the Call keyword before the method call.
As Macro Man mentioned, you can also omit the parentheses, instead of using Call:
MsgBox "Hello", Title:="test"
And it will work, too. This was the original vba coding style and isn't really used anymore.
What is the difference between these two lines?
Set MyMsg = MyMsg.Move(MyFolder2)
MyMsg.Move(MyFolder2)
The first one works just fine.
The second one usually gives an "Outlook is not responding" error.
The MailItem.Move method returns the MailItem that has been moved. Usually, properties return values and methods don't return anything. But for several methods, the designers decided it would be handy to have a return value, so they made them return a value (or object).
When you assign a method to a variable, any arguments must be in parentheses or you'll get a syntax error. If you call a method without assigning it to a variable (because you don't care what the method returns or it's one of the methods that doesn't return a value), then the arguments must not be in parentheses (kind of).
Parentheses, when used in places that the compiler does not require them, are the equivalent of saying "evaluate this before doing anything else". It's like how you use parentheses in order of operations so you can say "evaluate this addition operation before you do this multiplication even though that's not the normal order".
The (kind of) remark above is because most of the time when you "incorrectly" put parentheses around something, it doesn't matter.
Application.CreateItem 0
and
Application.CreateItem (0)
are the same. The second one evaluates the argument before it passes it to CreateItem, but evaluating a single integer takes no time and has no ill effects. The parentheses aren't necessary because we're not assigning the results to a variable, but they're not really hurting anything either.
In your second example, you're telling the compiler to evaluate the folder, then send it to the Move method. I don't know what evaluating a folder means, but I gather it's not good. It probably does something like create an array of all the objects in that folder, or something equally intensive. When Outlook is not responding, it means you gave it such a big job that it hasn't checked back in with the operating system in a timely fashion.
So: Use parentheses for arguments when it's on the right side of an equal sign. Don't use them when it's not. There are a few exceptions to that rule, but you may never need to know them.
There is no difference between the two (you just ignore the function result) unless you actually use the MyMsg variable afterwards - after the message is moved, you cannot access it anymore.
Use the first version.
I understand what IMPORTING and EXPORTING keywords do, but what is the significance of the CHANGING keyword?
IMPORTING passes an actual parameter as a formal parameter, thus transferring a value from the caller to the method. EXPORTING does the exact opposite, taking a value from the method and transferring it back to the caller. CHANGING combines these, transferring the value both from the caller to the method an back again, with any changes that happened in between.
Note that while IMPORTING and EXPORTING are reversed between declaration and call, CHANGING is not.
Also, when declaring Subroutines with FORM and ENDFORM, the CHANGING keyword can be used either like CHANGING myvar or CHANGING VALUE(myvar).
CHANGING myvar makes it so that the value of myvar is changed as soon as it is changed in the subroutine.
In contrast, if CHANGING VALUE(myvar) is used, if the form does not return properly (if it throws an exception by example), the value of myvar will remain unchanged, in the calling code, even if it was changed in the subroutine that crashed.