Holy cow, I wrote a book!
A customer noticed that environment variables were being
truncated at 2047 characters and wanted to know what
the maximum length of an environment variable is.
This is one of those cases where you can answer the customer's
question or you can try to answer the question the customer is
If you just answer the question, you're doing the customer a disservice.
The theoretical maximum length of an environment variable
is around 32,760 characters.
However, you are unlikely to attain that theoretical maximum in
All environment variables must live together in a single environment
block, which itself has a limit of 32767 characters.
But that count is the sum over all environment variable names and
so you could, I guess, hit that theoretical maximum length if
you deleted all the environment variables and then set a single
variable called X with that really huge 32,760-character value.
In practice, of course,
you have to share the environment block with all the other
variables in the block,
so your random call to SetEnvironmentVariable with
a 32,760-character string is unlikely to succeed.
But that's not your only practical limit.
It also depends on how you're setting the variable;
i.e., the code that your environment-variable-setting technique
passes through before it gets to the
If you're using a batch file,
then you're constrained by
the maximum command line length
since the environment variable needs to fit into the command line
buffer of the batch processor.
On the other hand, maybe you're setting the Environment registry key,
in which case
you run into a 2048-character limit
in the code that parses that registry key and builds an environment
block out of it.
There's also a limitation in the dialog box for interactively
setting environment variables, the numeric value of which I don't
happen to know off the top of my head.
This is one of those skills you have to develop when answering
questions from customers:
Taking the customer's question and trying to figure out
what their real question is.
A car park in Birmingham
switches from English to German in times of stress.
That reminded me that over a decade ago, a colleague of mine noticed
an error message on the screen at the exit to the parking garage at the
Seattle-Tacoma International Airport.
The way the airport works,
you pick up a ticket as you enter,
and you pay your parking fee at vending machines stationed around
the parking garage,
at the exit, you insert the (paid) ticket into the machine, which
verifies that you paid your parking fee and opens the gate.
When my colleague pulled up to the machine,
instead of displaying the expected
Please insert ticket message,
it had an error message.
Fortunately, my colleague knows German, and he recognized the
error message as a Windows 95 serial port conflict resolution dialog.
While he was trying to figure out how to click Abbrechen
on a machine with no mouse or keyboard,
an attendant walked up, took his ticket, and opened the gate.
Just another example which demonstrates that parking garage computers
are native speakers of German, and they revert to it when placed
in stressful situations.
It has been
famously said that
England and the United States are two countries separated by a common
The same holds true for Microspeak.
In the Redmond dialect of Microspeak,
we talk about extensibility:
Designing a system with specific points
where features can be added in the future,
often by outside parties.
an example of an extensibility point in the shell
would be a context menu handler or a namespace extension.
In the Reading dialect of Microspeak, the term for this is
On the other hand, if you use the term future-proofing
in Redmond, people will interpret it differently.
future-proofing means designing a system so that it continues
to function without alteration, even if something happens in the future.
(For example, one example of future-proofing—in the Redmond
sense of the term—would be using a function like
SHGetSpecialFolderPath instead of
hard-coding the path to a directory.)
Some folks have taken issue with this definition,
and I will have to defer to their local knowledge.
I get my reports on the Reading dialect of Microspeak from my contacts there,
so it's possible that they were mistaken,
or that the usage was peculiar to their workgroup and incorrectly
extrapolated to the entire dialect.
Surely by now
you've seen the video
where NextGenHacker101 shows you how
to use the "Tracer T" program to view "how many IP's are
looking at Google", their name, and connection speed (to then to then to then).
(And commenter squizz
explains why it "worked" in spite of the http prefix.)
But more awesome is the fact
that somebody ported the video to linux!
Bonus video craziness:
It's in Korean but somehow that just adds to the insanity.
(Warning: Five minutes of your life you will never get back.)
The comments on the YouTube video identify the song as
"Bo Peep Bo Peep <- possibly
the most addictive/annoying song ever."
For some time (I am too lazy to look up when it was introduced),
the Visual Studio linker has supported
known as delay-loading.
But why can't you use this feature to delay-load a function
It would be very handy:
If you write
the program fails to load on versions of Windows which do not
support the function Xyz
the Win32 load rejects loading a module that contains unresolved
On the other hand, if you could mark kernel32 as
delay-loaded, then the code above would work,
since the call to Xyz would be redirected to a stub
that calls GetProcAddress.
Since the GetProcAddress
is performed only when the code path is hit,
the loader won't complain at load time.
But if you try to delay-load kernel32,
the linker gets upset at you.
Why won't it let me delay-load kernel32?
The linker delay-load feature operates on the DLL level,
not on the function level.
When you put a DLL on the /DELAYLOAD list,
the linker changes all calls to functions in that DLL
into calls to linker-generated stubs.
These stubs load the target DLL, call GetProcAddress,
then resume execution at the target function.
Since the delay-load feature operates on the DLL level,
if you put kernel32
on the delay-load list,
then all calls to functions in kernel32
turn into calls to stubs.
And then you are trapped in this Catch-22.
When a function from kernel32 gets called,
transfer goes to the stub function, which loads the
target DLL (kernel32) to get the target function.
Except that loading the target DLL means calling
and finding the target function means calling
and these functions
themselves reside in kernel32.
Now you're trapped.
To load kernel32,
we need to call LoadLibrary,
but our call to LoadLibrary was redirected
to a stub which... calls LoadLibrary.
Sure, the linker folks could have added special casing for
kernel32, say, having a list of core functions
like InitializeCriticalSection which are
never delay-loaded and always go directly into kernel32.
But that's really out of scope for the /DELAYLOAD feature,
whose purpose is not to make it easier to call functions which
might not be there,
but rather to assist in application startup performance
by avoiding the cost of loading the target DLL until a function from
it is called.
If there were functions that went directly into kernel32,
then the stated purpose of delay-loading fails:
that import of InitializeCriticalSection forces
kernel32 to be loaded when the module is loaded,
completely contrary to the aim of delay-loading to avoid
loading kernel32 at module load time.
Now, it's certainly a nice feature to be able to perform
delay-loading on a per-function level,
in order to make it easier to write
code which changes behavior based on the current version of Windows,
but that's a different problem from what the /DELAYLOAD
switch was created to solve.