473,738 Members | 4,774 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Tricky VB/VBA side effects, and how to avoid them

== On Error Resume next, and Err.Number ==

If you want to call one of your procedures from another procedure, and check
for errors afterward, you mayimagine that you should write code something like
this...

On Error Resuse Next
MyFoo 123
lngErrNum = Err.Number
On Error Goto 0

.... so you do that, and it seems to work. A while later, you add code inside
MyFoo that does something similar, and now you find that the code above
sometimes detects an error when it shouldn't. This is because sometimes, the
error information never does get cleared after the error is skipped in the
called procedure, so it's still there when the calling procedure checks it.

I recommend engineering out this possibility at both ends. First, always do
an explicit Err.Clear after an "On Error Resuse Next" section, and second,
always trap for the occurrence of errors in calls to other procedures in the
same project using On Error Goto <label>, not On Error Resume Next, in spite
of the extra code involved.

== Passing object values to Variant parameters of procedures ==

Let's say you have a procedure that takes a Variant as a parameter, and adds
the parameter value to a collection. Now, let's say you call that procedure
from code in a form, and pass it, say, Me!txtName as an argument, then close
the form. Later, when you try to read the value back out of the collection,
you get an error. Why?

For convenience, VB allows certain objects to behave as values when you treat
them as values and as object references when you treat them as object
references, thus I can say either strName = Me!txtName to get the value from
the control, or Set ctlName = Me!txtName to make a reference to the control.

When you pass an object as a Variant parameter, there is nothing about the
syntax that tells VB whether to use the value or the object refence, and it
will always use the object reference in this case. This works fine only so
long as the procedure either expects an object, or does something internally
that causes the value interpretation of the object to be used while the
object's state is still valid.

This problem is really insidious since it can cause procedures to break after
a change that seems inconsequential , and can cause code to work some
circumetances, and not in others (e.g. work in testing, and fail when you demo
to the customer).

Again, I recommend solving the problem at both ends, just to cover all your
bases. When you want to pass the value of an object to a procedure, be
explicit about it, and pass something like Me!txtName.Valu e. When writing a
procedure that takes a variant and needs to deal only with a value, not an
object reference, convert any object reference you get to a plain value.

Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...

== Passing object references ByVal vs ByRef ==

Given the issue above, you may think you can solve the problem by defining
your function parameters as ByVal. That should always get the value of the
object, not the object reference, right? Wrong.

There are actually 2 levels of metaphor going on here.

First, for parameters that are passed by "reference" , the function uses an
indirect reference back to the variable in the calling procedure, so the
entire item does not have to be copied (a good thing for a string or an
array), and so that any changes made to the "value" (more on this below)
affect the value of the variable passed to the procedure in the calling code
as well.

Second, there are object type variables that have "references " to object
instances stored in memory, but these are -not- the same thing ByRef means by
"references ".

It turns out that what it actually means to pass an object variable by
reference or by value is that if you pass it by reference, assigning the
parameter variable a reference to a new object instance affects the variable
in the calling procedure as well, so that it now also points to the new object
instance.

When passing by value, a separate, new reference to the same object instance
is created in the called procedure, so that changes made to the state of the
instance are still shared between the caller and called procedure (the 2
references are to the same instance), but assigning a new instance to the
parameter variable within the called procedure does not also affect the
variable in the calling procedure (the 2 references are now to different
instances).

== ByRef and side effects ==

Parameters in VB/VBA are ByRef if you don't specify, and that means that a
change made to the parameter in a called procedure affect the calling
procedure. No new news here. What that means, though, is that you really
have to be careful because it's common to use a parameter as a variable with
an initial state that can then be changed as part of the procedure's process.
if you don't remember to make the parameter ByVal, you just accidentally
mangled a value in the calling procedure, and this kind of bug can be really
tricky to track down.

I recommend yet another 2-pronged attack on this problem. First, always
specify ByVal for any parameter that has no specific, good reason to be ByRef
(and performance is only a good reason when you are having or expect to have
an actual performance problem). Second, never, ever assign a value to a
parameter variable unless it actually is for the purpose of returning a value
to the calling procedure through a ByRef parameter. In all other cases, use
local Dim'd variables for any values that can be changed during the execution
of the procedure.
Nov 13 '05 #1
13 2753
Steve Jorgensen <no****@nospam. nospam> wrote in
news:7s******** *************** *********@4ax.c om:
Again, I recommend solving the problem at both ends, just to cover
all your bases. When you want to pass the value of an object to a
procedure, be explicit about it, and pass something like
Me!txtName.Valu e. When writing a procedure that takes a variant
and needs to deal only with a value, not an object reference,
convert any object reference you get to a plain value.

Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...


Does putting () around an object cause the evaluated value to be
passed?

Wouldn't using ByVal fix the problem, too, assuming you don't want
to operate on the value itself (which you wouldn't really be able to
do with .Value, anyway)?

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #2
Steve Jorgensen <no****@nospam. nospam> wrote in
news:7s******** *************** *********@4ax.c om:
== Passing object references ByVal vs ByRef ==

Given the issue above, you may think you can solve the problem by
defining your function parameters as ByVal. That should always
get the value of the object, not the object reference, right?
Wrong.

There are actually 2 levels of metaphor going on here.

First, for parameters that are passed by "reference" , the function
uses an indirect reference back to the variable in the calling
procedure, so the entire item does not have to be copied (a good
thing for a string or an array), and so that any changes made to
the "value" (more on this below) affect the value of the variable
passed to the procedure in the calling code as well.

Second, there are object type variables that have "references " to
object instances stored in memory, but these are -not- the same
thing ByRef means by "references ".

It turns out that what it actually means to pass an object
variable by reference or by value is that if you pass it by
reference, assigning the parameter variable a reference to a new
object instance affects the variable in the calling procedure as
well, so that it now also points to the new object instance.

When passing by value, a separate, new reference to the same
object instance is created in the called procedure, so that
changes made to the state of the instance are still shared between
the caller and called procedure (the 2 references are to the same
instance), but assigning a new instance to the parameter variable
within the called procedure does not also affect the variable in
the calling procedure (the 2 references are now to different
instances).


Steve, I don't understand this -- I can't quite get your point.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #3
On Wed, 01 Sep 2004 19:17:05 GMT, "David W. Fenton"
<dX********@bwa y.net.invalid> wrote:
Steve Jorgensen <no****@nospam. nospam> wrote in
news:7s******* *************** **********@4ax. com:
Again, I recommend solving the problem at both ends, just to cover
all your bases. When you want to pass the value of an object to a
procedure, be explicit about it, and pass something like
Me!txtName.Valu e. When writing a procedure that takes a variant
and needs to deal only with a value, not an object reference,
convert any object reference you get to a plain value.

Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...
Does putting () around an object cause the evaluated value to be
passed?


Yes, but that reads strangely when you are only passing one parameter. If
it's a Sub call, it looks like you put in parenthes that don't belong there,
and if you call a function, it looks liek you have an extra set. Either way,
you or someone else maintaining the code might end up taking them out later.
Wouldn't using ByVal fix the problem, too, assuming you don't want
to operate on the value itself (which you wouldn't really be able to
do with .Value, anyway)?


In another section of my post, I tried (not very well, apparently) to explain
why it turns out that it would not, though I've only understood this for a
short time, myself. When objects are involved, ByVal is misnamed, because it
doesn't force the object's value to be evaluated, it just makes a new
reference to the object instead of sharing the calling procedure's reference
to the object.

If that makes sense, then it answers the question in your other post as well.
Perhaps, an example would help, though.

Let's assume my form has controls txtValue1, containing "aaa" and txtValue2
containing "bbb". The following code doesn't resemble anything you would
actually do, but it illustrates the behavior I'm trying to explain.

First, here's the output from the code below...

txtValue1=aaa txtValue2=bbb
txtValue1=aaa txtValue2=xxx
ctl.Name=txtVal ue2
txtValue1=yyy txtValue2=zzz
ctl.Name=txtVal ue2

Now, the code...

Private Sub Foo()
Dim ctl As Access.Control

Debug.Print "txtValue1= " & Me!txtValue1, _
"txtValue2= " & Me!txtValue2

Set ctl = Me!txtValue1
Bar ctl
' Me!txtValue2 now contains "xxx"
' ctl now contains a reference to Me!txtValue2
Debug.Print "txtValue1= " & Me!txtValue1, _
"txtValue2= " & Me!txtValue2
Debug.Print "ctl.Name=" & ctl.Name

Set ctl = Me!txtValue2
Baz ctl
' Me!txtValue1 now contains "xxx"
' ctl still contains a reference to Me!txtValue2
Debug.Print "txtValue1= " & Me!txtValue1, _
"txtValue2= " & Me!txtValue2
Debug.Print "ctl.Name=" & ctl.Name

End Sub

Private Sub Bar(varValue As Variant)
' Because parameter is by reference, the next line changes
' what control the calling code's parameter variable points
' to as well.
Set varValue = Me!txtValue2
varValue.Value = "xxx"
End Sub

Private Sub Baz(ByVal varValue As Variant)
' ByVal gave us a separate, local reference to the object,
' but it's still an object reference, not a value.
varValue.Value = "zzz"
' Because parameter is by ByVal, the next line only affects
' the state of the local varValue.
Set varValue = Me!txtValue1
varValue.Value = "yyy"
End Sub

Nov 13 '05 #4
> Does putting () around an object cause the evaluated value to be
passed?

Wouldn't using ByVal fix the problem, too, assuming you don't want
You do get tricky VB/VBA side effects :~)

For example, this does not just pass a string value:

call sb((codedb.name ))

private sub sb(byval s as string)

.... for some reason it actually does retain a
reference to the temporary db object, which you
can see inside sub sb if you iterate through
the databases collection.

This has a quite different effect:
s = codedb.name
call sb(s)

(david)

"David W. Fenton" <dX********@bwa y.net.invalid> wrote in message
news:Xn******** *************** ***********@24. 168.128.74... Steve Jorgensen <no****@nospam. nospam> wrote in
news:7s******** *************** *********@4ax.c om:
Again, I recommend solving the problem at both ends, just to cover
all your bases. When you want to pass the value of an object to a
procedure, be explicit about it, and pass something like
Me!txtName.Valu e. When writing a procedure that takes a variant
and needs to deal only with a value, not an object reference,
convert any object reference you get to a plain value.

Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...


Does putting () around an object cause the evaluated value to be
passed?

Wouldn't using ByVal fix the problem, too, assuming you don't want
to operate on the value itself (which you wouldn't really be able to
do with .Value, anyway)?

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc

Nov 13 '05 #5
On Thu, 2 Sep 2004 12:24:17 +1000, "david epsom dot com dot au"
<david@epsomdot comdotau> wrote:
Does putting () around an object cause the evaluated value to be
passed?

Wouldn't using ByVal fix the problem, too, assuming you don't want


You do get tricky VB/VBA side effects :~)

For example, this does not just pass a string value:

call sb((codedb.name ))

private sub sb(byval s as string)

... for some reason it actually does retain a
reference to the temporary db object, which you
can see inside sub sb if you iterate through
the databases collection.

This has a quite different effect:
s = codedb.name
call sb(s)

(david)


To me, that is less of a side effect than what I'm talking about. When a
dynamic object reference is created within a statement, the reference is
always held until the statement completes. This is actually to prevent side
effects such as what would happen if you tried to pass a tabledef to a
procedure using Foo CurrentDb.Table Defs("tblBar"). If the dynamic reference
to the database instance returned by CurrentDb were allowed to go out of scope
immediately, then Foo would not be able to use the TableDef reference that was
passed to it. You can think of the behavior as being similar to a With block.
Nov 13 '05 #6
rkc

"Steve Jorgensen" <no****@nospam. nospam> wrote in message
news:cj******** *************** *********@4ax.c om...
In another section of my post, I tried (not very well, apparently) to explain why it turns out that it would not, though I've only understood this for a
short time, myself. When objects are involved, ByVal is misnamed, because it doesn't force the object's value to be evaluated, it just makes a new
reference to the object instead of sharing the calling procedure's reference to the object.


What would an object's value be evaluated to? Objects created with
VBA have no way to set a defaut value.

Nov 13 '05 #7
On Thu, 02 Sep 2004 03:22:39 GMT, "rkc" <rk*@yabba.dabb a.do.rochester. rr.bomb>
wrote:

"Steve Jorgensen" <no****@nospam. nospam> wrote in message
news:cj******* *************** **********@4ax. com...
In another section of my post, I tried (not very well, apparently) to

explain
why it turns out that it would not, though I've only understood this for a
short time, myself. When objects are involved, ByVal is misnamed, because

it
doesn't force the object's value to be evaluated, it just makes a new
reference to the object instead of sharing the calling procedure's

reference
to the object.


What would an object's value be evaluated to? Objects created with
VBA have no way to set a defaut value.


Of course, this whole discussion can only refer to cases where you're passing
around references to objects that do have default value properties. While you
can't (without cheating) create these yourself in VBA, there are many cases
where we use and pass sround existing objects that do have default value
properties, such as fields and controls. It is when we pass these as
arguments to functions that the issues arise.
Nov 13 '05 #8
On Thu, 02 Sep 2004 09:22:50 GMT, Chuck Grimsby
<c.*******@worl dnet.att.net.in valid> wrote:
On Wed, 01 Sep 2004 10:26:36 GMT, Steve Jorgensen
<no****@nospam .nospam> wrote:
== On Error Resume next, and Err.Number ==

<snip>
Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...


When doing this, use a function, not a sub, and return back the error
number so that the calling function can react to the error.


I'm not sure why that's better.
1. The onus on the error trapping has now simply moved from the calling code
into the called code.
2. Perhaps the procedure to be called is already, logically a function, not a
sub, that returns something else besides an error number as its value. In
other words, we already have a communication channel for error information,
why use up another communication channel that we might want for something
else?
3. What if not every caller expects to repond directly to an error? Perhaps,
the error handling is better done farther up the call stack.

Nov 13 '05 #9
On Thu, 02 Sep 2004 22:51:46 GMT, Chuck Grimsby
<c.*******@worl dnet.att.net.in valid> wrote:
On Thu, 02 Sep 2004 20:27:50 GMT, Steve Jorgensen
<no****@nospam .nospam> wrote:
On Thu, 02 Sep 2004 09:22:50 GMT, Chuck Grimsby
<c.*******@wo rldnet.att.net. invalid> wrote:
On Wed, 01 Sep 2004 10:26:36 GMT, Steve Jorgensen
<no****@nosp am.nospam> wrote:

== On Error Resume next, and Err.Number ==
<snip>
Sub Foo(varValue As Variant)
Dim varUseValue As Variant
varUseValue = varValue
...When doing this, use a function, not a sub, and return back the error
number so that the calling function can react to the error.

I'm not sure why that's better.
1. The onus on the error trapping has now simply moved from the calling code
into the called code.
2. Perhaps the procedure to be called is already, logically a function, not a
sub, that returns something else besides an error number as its value. In
other words, we already have a communication channel for error information,
why use up another communication channel that we might want for something
else?
3. What if not every caller expects to repond directly to an error? Perhaps,
the error handling is better done farther up the call stack.


When you have the error number, the calling code can react in a
"custom" method rather then a general. As you well know, all sorts of
bizarre things can happen to objects as they are passed around and
manipulated by various subs and functions. Especially if the code
called is doing it's own error handling that the calling code will
never know about.


I'm not sure I get what you're getting at. The calling code can respond the
the error in a custom way by trapping the error as well, without needidg the
return value. Admittedly, that takes more code in the calling procedure, but
you're now putting the onus on the called procedure to know that the calling
procedure will want to know the error code, and will want to know it
disticntly for that call and not generically for a series of statements (say,
3 calls within a loop).

Further more, you must either...
A) assume every caller will want error information returned as a parameter,
and always do that, then have to return any other data via reference
parameters which often increases the mumber of lines of code requireds in the
calling procedure
B) modify each procedure when you find out that a calling procedure will want
to handle error information distinctly for that procedure. In this case, you
may break other code that was trapping for errors, and expecting an error
branch if one did occur.

Also, if the called procedure is trapping its own error, how likely is it that
the calling procedure will actually need to know which error it was. Perhaps,
a mere boolean success/failure code would be a more appropriate thing to
return.

I think it's more consistent to try to use the exception handling mechanisms
for error trapping, and use function return values to return processing output
from function procedures.

Here's some thinking out-loud about a sort of compromise between our 2
positions...

Add an optional ByRef Variant parameter to the called procedure that can be
used to retrieve its exception information. Have the called procedure's error
handler re-raise the exception if the variant parameter IsMissing(...), or
pass the error info back to the calling procedure via the parameter if it is
not missing.

Now, the calling procedure can either use error trapping or not as desired,
and it can avoid error trapping without the use of On Error Resume Next simply
by passing an argument to collect the error information. This can be used
even with a function procedure that has reason to return something other than
error information as its return value. One potential source of trouble here
is that the error info parameter must be initialized, and that's easy to
forget. I guess that's an advantage to the return value as opposed to the
ByRef argument.
Nov 13 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
2388
by: Sensorflo | last post by:
After browsing though many newsgroups articels I'm still not shure how operator precedence, operator associativity, sequence points, side effects go together. Currently I have the following view: An expression a = b() + c() * d++; can be transformed with the rules of operator associativity and operator precedence into a tree
23
2368
by: Mantorok Redgormor | last post by:
Can emulation of the logical OR be done in standard C to obfuscate its use? So I don't have to use if(a||b) but instead make that even more obfuscated without its use but testing the same condition
9
6894
by: Rouben Rostamian | last post by:
Consider the following illustrative program: #include <stdio.h> double f(double x) { return x*x; } double g(double x)
5
3226
by: Niklaus | last post by:
This is one of the posts that i got. ------------------------------ A "side effect" of an operation is something that *happens*, not something that *is produced*. Examples: In the expression 2+2, the value 4 *is produced*. Nothing *happens*. Thus, 4 is the value of the expression, and it has no side effects. In the expression g=2.0, the value 2.0 is produced. What *happens* is that 2.0 is assigned to g. Thus, 2.0 is the value of the...
4
1542
by: andrew browning | last post by:
does i++ have the same potential for side effects as its pre-fix equivilent ++i? and if so, is it always dangerous to use them, as in a for loop, or only in certain circumstances?
6
4916
by: Senthil | last post by:
Hi, Whenever i read a C++ book or a newsgroup posting, i come across the terms like " before the side effects completes" , "destructor with side effects" etc. What is this side effect mean in C++ world? Is it like something that is not desired but happens automatically and we cannot prevent it? Would be great if someone throws light on this. Thanks,
6
2421
by: Michael B Allen | last post by:
Hi, I have a macro that looks like the following: #define MMSG msgno_loc0(LOC0, LOC1) && msgno_mmsg0 which if used in some code like: MMSG("foo=%s", foo);
4
3033
by: Academia | last post by:
I get the following watch message: tsSource.Text.ToUpper() This expression causes side effects and will not be evaluated string The Text is &Edit
10
1762
by: mirandacascade | last post by:
Question toward the bottom of this post....background information immediately below. Access 97 SQL Server 2000 Please note: although the subject line uses the word 'bloat', this post is NOT a "what can I do to prevent bloat?" inquiry. When I searched the postings in this group for information about bloat, I believe I gained a rudimentary understanding of some of the things
0
8969
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9476
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
9263
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9208
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8210
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6053
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
1
3279
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2745
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2193
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.