473,761 Members | 3,542 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
13 2760
On Fri, 03 Sep 2004 08:36:21 GMT, Chuck Grimsby
<c.*******@worl dnet.att.net.in valid> wrote:
....
I'm not really too sure what you're getting at here either, Steve. If
an error is handled by the sub and cleared, how the heck would the
calling code know there was an error at all? If the calling code
*did* need to know an error occurred, how are you proposing to have
the calling code made aware of that? If I'm reading your post
correctly, then it's your position that there should be no need.
Whatever code needed to handle any and all problem(s) should be in the
Sub, not in the calling code?


Looking back at my post, I'm not surprised, because that's one long and
convoluted discussion. Let's see if I can write more concisely.

First off, no I'm not saying anything categorically about whether error
handling should be in calling code or called code (everything's called code at
some level). I'm just saying that, in general, if a calling procedure needs
to know about an exception that happens in a called procedure, it should use
On Error Goto to handle it.

This is in contradiction to your suggestion that error infomation be passed as
the return value from a function. This is unattractive to me because it seems
to be ignoring a feature that was added to VB specifically to provide a
channel for error information that leaves other channels such as function
return values free to handle other non-exceptional things.

I didn't say this part before, but if the called procedure also needs to do
clean-up in the event of an error, it should do so, then raise the error
again, so the calling code can capture it. I often use a class instance to
store the error info and re-raise it. This class also becomes a convenient
place to put other error handling details, such as logging, message boxes,
etc.

Anyway, in thinking about the difference between our points of view, I had a
thought that one could write a procedure that would sort of work either way.
The procedure would re-raise any errors that occur within it that need to be
handled externally, but only so long as it was not given an optional ByRef
argument that could be used to pass the error information back to the caller.
If it did get that argument, it would pass the error info through that
parameter, and not re-raise the error.

Of course, all of this ignores the cases of exceptions that can be fully
handled within the called procedure, and the calling procedure should not need
to know about. These, of course, should never be returned to the caller, no
matter what scheme is used. I assume we're on the same page on this point.
Nov 13 '05 #11
rkc

"Steve Jorgensen" <no****@nospam. nospam> wrote in message
news:ag******** *************** *********@4ax.c om...
On Fri, 03 Sep 2004 08:36:21 GMT, Chuck Grimsby
<c.*******@worl dnet.att.net.in valid> wrote:
...
I'm not really too sure what you're getting at here either, Steve. If
an error is handled by the sub and cleared, how the heck would the
calling code know there was an error at all? If the calling code
*did* need to know an error occurred, how are you proposing to have
the calling code made aware of that? If I'm reading your post
correctly, then it's your position that there should be no need.
Whatever code needed to handle any and all problem(s) should be in the
Sub, not in the calling code?


Looking back at my post, I'm not surprised, because that's one long and
convoluted discussion. Let's see if I can write more concisely.


The discussion was a bit hard to follow so I may not fully understand what
you
were getting at. What it left me with is a feeling that most of the problems
cited
could be reduced by a move away from convoluted functional based design
toward a more object oriented approach.


Nov 13 '05 #12
> an error is handled by the sub and cleared, how the heck would
calling code know there was an error at all? If the calling
FWIW, I don't clear errors in subroutines. I re-raise
them. My standard error code looks like this:

catch:
gsbErr_Raise mcModuleName, "subName"

At the top level (if it gets that far), a custom message
box displays the error description, suggested response,
and debug stack.
The exception can be suppressed/ignored/corrected at
an stage between the try/catch function and the reporting
function.

(david)

"Chuck Grimsby" <c.*******@worl dnet.att.net.in valid> wrote in message
news:2q******** *************** *********@4ax.c om... On Fri, 03 Sep 2004 00:26:37 GMT, Steve Jorgensen
<no****@nospam. nospam> wrote:
On Thu, 02 Sep 2004 22:51:46 GMT, Chuck Grimsby
<c.*******@wor ldnet.att.net.i nvalid> wrote:
On Thu, 02 Sep 2004 20:27:50 GMT, Steve Jorgensen
<no****@nospa m.nospam> wrote:

On Thu, 02 Sep 2004 09:22:50 GMT, Chuck Grimsby
<c.*******@w orldnet.att.net .invalid> wrote:

>On Wed, 01 Sep 2004 10:26:36 GMT, Steve Jorgensen
><no****@nos pam.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 codeinto the called code.
2. Perhaps the procedure to be called is already, logically a function, not asub, that returns something else besides an error number as its value. Inother words, we already have a communication channel for error information,why use up another communication channel that we might want for somethingelse?
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 thethe error in a custom way by trapping the error as well, without needidg thereturn value. Admittedly, that takes more code in the calling procedure, butyou're now putting the onus on the called procedure to know that the callingprocedure 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 thecalling procedure
B) modify each procedure when you find out that a calling procedure will wantto handle error information distinctly for that procedure. In this case, youmay 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 thatthe 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 mechanismsfor error trapping, and use function return values to return processing outputfrom 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 beused to retrieve its exception information. Have the called procedure's errorhandler re-raise the exception if the variant parameter IsMissing(...), orpass the error info back to the calling procedure via the parameter if it isnot 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 simplyby passing an argument to collect the error information. This can be usedeven with a function procedure that has reason to return something other thanerror information as its return value. One potential source of trouble hereis 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 theByRef argument.


I'm not really too sure what you're getting at here either, Steve. If
an error is handled by the sub and cleared, how the heck would the
calling code know there was an error at all? If the calling code
*did* need to know an error occurred, how are you proposing to have
the calling code made aware of that? If I'm reading your post
correctly, then it's your position that there should be no need.
Whatever code needed to handle any and all problem(s) should be in the
Sub, not in the calling code?
--
A Programmer And His Mind Are Soon Parted

Nov 13 '05 #13
On Mon, 6 Sep 2004 09:54:44 +1000, "david epsom dot com dot au"
<david@epsomdot comdotau> wrote:
an error is handled by the sub and cleared, how the heck would
calling code know there was an error at all? If the calling


FWIW, I don't clear errors in subroutines. I re-raise
them. My standard error code looks like this:

catch:
gsbErr_Raise mcModuleName, "subName"

At the top level (if it gets that far), a custom message
box displays the error description, suggested response,
and debug stack.
The exception can be suppressed/ignored/corrected at
an stage between the try/catch function and the reporting
function.

(david)


Well, the reason I came across this issue in the first place is that,
sometimes an error is completely handled within the called function, so
there's no reason to let the calling function know about it. The problem is
that, if the called function tries an operatino that might raise an error
using On Error Resume Next, then checks Err.Number, if it doesn't also clear
the error, and if the calling procedure tries the same thing when calling the
called procedure, it may see that same error information still in the Err
object, and think this was an unhandled or re-raised error from the called
procedure.

That's why I say to block this problem at both ends, by clearing any error
information after an On Error Resume Next block in the called procedure
(assuming the error is handled locally and there is no reason to re-raise it),
and by having the calling procedure never try to identify errors from a called
procedure in the same project with On Error Resume Next, only with On Error
Goto <label>.
Nov 13 '05 #14

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
2378
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
6898
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
3230
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
1543
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
4917
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
2422
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
3038
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
1764
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
9377
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
9925
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
9811
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
8814
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...
1
7358
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6640
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();...
0
5266
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
3913
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
3
2788
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.