By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,105 Members | 2,569 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,105 IT Pros & Developers. It's quick & easy.

A97: Hint About Constants, Compiling, Decompile

P: n/a
Today I was working on a hideous old app that I created a long time
ago that does a lot of showing/hiding/resizing of fields on one of
the forms. I had used constants to store reference values for many
of the top/height/left settings. I noticed that some of the
constants were defined by using other constant values, and I was
impressed that VBA could do that. Here's an example:

Const row1Top = 1.0208 * 1440 ' top of first row
Const row2Top = row1Top + 0.2083 * 1440
Const row3Top = row2Top + 0.2083 * 1440
Const row4Top = row3Top + 0.2083 * 1440
Const row5Top = row4Top + 0.2083 * 1440
Const row6Top = row5Top + 0.2083 * 1440
Const row7Top = row6Top + 0.2083 * 1440

Anyway, another set of constants were related like this (in rows),
but they were not defined in terms of each other. It was so complex
that I couldn't figure out what they were, but I was able to
determine that I could simply add to them the amount that I wanted
to move them (I'd made the form bigger, moving some controls that
were at the top over to the side, so everything had to be moved
up). So, I created an offset constant and used that to adjust all
those constants:

Const bksCommtHeight = ((memoHeightOffset + 0.5) * 1440) + 250
Const memoHeightOffset = 0.94 '0.8354
Const memo3Tall = ((memoHeightOffset + 1.4146) * 1440) + 160
Const memo4Tall = ((memoHeightOffset + 1.2063) * 1440) + 160
Const memo5Tall = ((memoHeightOffset + 0.9979) * 1440) + 160
Const memo6Tall = ((memoHeightOffset + 0.7896) * 1440) + 160
Const lblOffset = -0.3291 ' adjusts memo label top for new layout
Const lblMemoTop3 = ((lblOffset + 1.9167) * 1440) - 480 - 100
Const lblMemoTop4 = ((lblOffset + 2.125) * 1440) - 480 - 55
Const lblMemoTop5 = ((lblOffset + 2.3333) * 1440) - 480 - 100
Const lblMemoTop6 = ((lblOffset + 2.5417) * 1440) - 480 - 100
Const lblMemoTop7 = ((lblOffset + 2.75) * 1440) - 480 - 100

This all compiled just great with no problems.

But when I was preparing it to ship it off to the client, I
decompiled and recompiled, and then I found that the order of the
constant declarations now suddenly mattered: memoHeightOffset was
used immediately above it in the declaration of bksCommtHeight
(don't ask me where I got these "naming conventions" -- this code
dates from back before I realized those things helped make code
more manageable!). When I moved memoHeightOffset above
bksCommtHeight, the code would then compile.

The point here is that this is one very specific case where
conditional compiling may not throw an error when it really should.
In this case, it won't make your code break, but it is the kind of
thing that would bother me. If your compilation gets discarded, the
code *will* break when you try to compile it.

Interestingly, this doesn't break the creation of an MDE *if*
you've successfully compiled *before* you create the MDE. What this
shows is that creating and MDE does not discard previously compiled
code.

To see this in action, past these constants into a module:

Line 1. Global Const c1 = 1
Line 2. Global Const c2 = c1 + 1

Compile.

Then on Line 0, paste this:

Global Const c3 = c1 + 2

Then compile.

It will successfully compile.

Now, decompile and try to recompile, and this time it won't compile
until you put the declaration of c3 after the declaration of c1.

To see the problem with an MDE, paste in the declarations of c1 and
c2, then compile, then paste the declaration of c3 *before* c1 and
*don't* compile. Save and then try to create an MDE -- it won't
work. But if you do exactly the same thing and compile before
attempting to create the MDE, it will succeed.

I would actually prefer that one of two things would happen here:

1. either constants could not be defined with other constants at
all (which would make my old application much harder to program),
OR

2. VBA should strictly require that constants be declared in order
within a module.

Of course, I can see how that could be problematic. How would you
enforce order of dependency for constants that are defined in
different modules? Well, it turns out that VBA allows a constant in
one module to be based on a constant defined in another module, but
it won't allow a further reference back to the second module. In
other words:

Module 1 Const c1 = 1

Module 2 Const c2 = c1 + 1

As long as the dependencies are linear, there is no problem: Module
3 can have a constant defined with a constant from module 2 which
depends on module 1. But as soon as you have a circular dependency
of modules, it can't work.

It's obvious why if you have this:

Module 1 Const c1 = 1

Module 2 Const c2 = c1 + 1

Module 1 Const c3 = c2 + 1

Since constants are compiled to literal values within the compiled
code, c1 could be replaced with 1 in Module 1 and in module 2. But
the value of c2 (which evaluates to 2) cannot be compiled because
module 2 cannot calculate the value of c1 because module 1 can't be
compiled because it has a constant declaration that depends on
module 2 having been already compiled.

I tested this and found that compiling after inserting each
declaration does not have any effect, and it's obvious why not:
it's because module 1 cannot be fully compiled without module 2
being compiled first, and module 2 can't be compiled until module 1
is compiled.

Now, that's not nearly as complex as it seems, but what it shows is
that order of declaration of dependent constants only matters
WITHIN A SINGLE MODULE. Constants defined based on other constants
in different modules will work fine as long as there's no
circularity.

Within a module, VBA resolves the circularity only if the code has
been pre-compiled.

I'm kind of surprised that VBA does not just figure out what it
needs and then figure out the order of it. In my example,

Line 0. Global Const c3 = c1 + 2
Line 1. Global Const c1 = 1
Line 2. Global Const c2 = c1 + 1

the actual order of the code shouldn't really matter. Constants
defined literally could be resolved first, then that would allow c3
to be compiled. If my example had been:

Line 0. Global Const c3 = c2 + 2
Line 1. Global Const c1 = 1
Line 2. Global Const c2 = c1 + 1

The same thing could have worked, but it would have to be chained,
with c1 being resolved first, then it would be possible to resolve
c2 and then c3.

I guess I can see why they wouldn't bother with this, since,
theoretically, there's no limit to the number of dependency levels,
and it would mean multiple compile levels and a fairly complex
evaluation of the code to resolve dependencies. As it is, if the
code is already partly compiled, that partly compiled code is used
and then the out-of-order declaration doesn't matter.

That' user-friendly, but to me, seems dangerous, as I can think of
plenty of scenarios where I might ship code that has to be
decompiled and recompiled remotely by someone other than me (or by
me 5 or 6 years after the code was written, as in the case that
caused me to discover this!).

So, my lessons from this:

1. don't define constants in terms of other constants unless
there's an obvious benefit to doing so, as in the first case, with
the row constants. Of course, these constants:

Const row1Top = 1.0208 * 1440 ' top of first row
Const row2Top = row1Top + 0.2083 * 1440
Const row3Top = row2Top + 0.2083 * 1440
Const row4Top = row3Top + 0.2083 * 1440
Const row5Top = row4Top + 0.2083 * 1440
Const row6Top = row5Top + 0.2083 * 1440
Const row7Top = row6Top + 0.2083 * 1440

could have been replaced by:

Const row1Top = 1.0208 * 1440
Const rowOffset= 0.2083 * 1440

And then rows 2-7 could be calculated with:

top = row1Top + (N-1 * rowOffset)

where N is th number of the row. So, I'm not sure there's ever
really any justification for it, though the vertical offset seems
like one:

Const memoHeightOffset = 0.94 '0.8354
Const memo3Tall = ((memoHeightOffset + 1.4146) * 1440) + 160
Const memo4Tall = ((memoHeightOffset + 1.2063) * 1440) + 160
Const memo5Tall = ((memoHeightOffset + 0.9979) * 1440) + 160
Const memo6Tall = ((memoHeightOffset + 0.7896) * 1440) + 160

There's no formula for me to get that, but that may be more a
legacy issue where I no longer understand how I reached those
numbers.

2. if you *do* define constants based on other constants within a
module, then make sure you do them in order, as then you don't need
to worry about order of compiling and precompiled code.

The real eye-opener here, though, is the realization that an MDE
does not freshly recompile everything.

My suggestion based on that:

Always decompile and then recompile before attempting to create an
MDE.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #1
Share this question for a faster answer!
Share on Google+

This discussion thread is closed

Replies have been disabled for this discussion.