February, 2010

  • The Old New Thing

    What is the maximum length of an environment variable?


    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 really asking. 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 practice.

    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 values, 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 SetEnvironmentVariable call. 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.

  • The Old New Thing

    No matter what you do, someone will call you an idiot, part 2


    There was quite a bit of reaction to what I thought was a simple "Hey, here's what's going on" article from last year, specifically on how the Adaptive Display Timeout means that Windows doesn't always start the screen saver exactly on time. As you may recall, this feature adjusts the time it takes for the screen saver to activate if the user keeps dismissing it immediately after it starts. One of those small things that makes the computer adapt to you rather than vice versa, and an adaptation that you probably don't even notice when it happens.

    I think these two responses below summarize the extremes of the types of reactions this feature generated.

    • Vista/W7 has some nice touches too. http://is.gd/2FgKv Such little things is what we love about Macs.

    • Ich finde das erstaunlich. In einem Linux-System kann man soetwas relativ leicht selber scripten, und sich z.B. einen Button erzeugen, der den Bildschirmschoner kurzzeitig ganz ausschaltet. Sowas geht unter Windows freilich auch, aber kaum ein User kennt das System hinreichend gut. Deshalb stellen sich dort solche Probleme. Erstaunlich.

      Translation: This is incredible. On Linux, you can script this yourself relatively easily and create, for example, a button which completely disables the screen saver for a short time. Admittedly, you can also do this on Windows, but hardly any users know the system that well. And therefore we have these types of problems. Incredible.

    On the one hand, the feature is something so cool, it must have been stolen from a Mac.

    On the other hand, this feature shows everything that is wrong with Windows. I mean, on Linux, you can solve this problem by simply writing a script!

  • The Old New Thing







  • The Old New Thing

    How do I get information about the target of a symbolic link?


    Functions like GetFileAttributes and FindFirstFile, when asked to provide information about a symbolic link, returns information about the link itself and not the link destination. If you use the FindFirstFile function, you can tell that you have a symbolic link because the file attributes will have the FILE_ATTRIBUTES_REPARSE_POINT flag set, and the dwReserved0 member will contain the special value IO_REPARSE_TAG_SYMLINK.

    Okay, great, so now I know I have a symbolic link, but what if I want information about the link target? For example, I want to know the size of the link target, its last-modified time, and its name.

    To do this, you open the symbolic link. The I/O manager dereferences the symbolic link and gives you a handle to the link destination. You can then call functions like GetFileSize, GetFileInformationByHandleEx, or GetFinalPathNameByHandle to obtain information about the symbolic link target.

    Exercise: If the field is called dwReserved0, shouldn't it be off limits? Why isn't the field called dwReparsePointType?

  • The Old New Thing

    Advocating the overthrow of the government of the United States by force or subversion


    It has been widely reported that South Carolina now requires "subversive groups" to register with the Secretary of State (and pay a $5 filing fee).

    Curiously, the list of organizations which must register include "an organization subject to foreign control." I wonder if this means that all consulates have to register, and that when any foreign dignitary visits South Carolina, they have to pay a $5 filing fee. (Not to mention all foreign-owned companies like Shell Oil.)

    Actually, it has been pointed out that a "subversive organization" includes one which advocates, teaches, or practices the propriety of controlling the government of the United States. I guess this means all political parties are subversive organizations. (Something most of us knew already.)

    And apparently, in your registration, you also have to include the bylaws or minutes of meetings from the last year. I wonder whether you have to resubmit the minutes each year. I'm sure somebody could keep a government bureaucrat busy for a long time by submitting hundreds of pages of "minutes".

    Anyway, this is a long and largely superfluous set-up for a different story. The mother of a colleague of mine came to visit from Canada. For some reason, the United States requires visitors to fill out a questionnaire asking them whether they are a drug dealer, whether they are a Nazi war criminal, and this question:

    Do you advocate the overthrow of the United States government by force or subversion?

    The sweet old lady studied the question for a while, then circled force.

    Bonus weirdness: On the form, it also says "Answering Yes will not necessarily exclude you from admission to the United States."

  • The Old New Thing

    How many servings are there in a single-serve cup? The answer might surprise you



    Delight in a cup
    Your favorite XYZ Ice Cream
    Now in convenient single-serve cups.


    Servings per container: 2
  • The Old New Thing

    Don't forget to double-null-terminate those strings you pass to SHFileOperation


    About once every two months for the past six months (I stopped checking further back), somebody reports a problem with the SHFileOperation function. Often, they don't include very much information at all. They just say, "I call the function and it doesn't work." Here's an example:

    I'm hitting a problem with SHFileOperation when using it to frob files in the gonzo directory when the user's SID ends in an odd number.

        // Delete the file.
        // szDeletePath contains the full path to the file.
        shFileOp.hwnd = NULL;
        shFileOp.wFunc = FO_DELETE;
        shFileOp.pFrom = szDeletePath;
        shFileOp.pTo = NULL;
        shFileOp.fFlags = FOF_NO_UI;
        iRet = SHFileOperation( &shFileOp );

    The function returns file not found, but the file is definitely there.

    If you read the variable names carefully, you can see the problem.

    The pFrom and pTo members of the SHFILEOPSTRUCT structure are double-null-terminated strings. (There's even a callout box for this in the MSDN documentation.) But a variable named szDeletePath is probably a single-null-terminated string. (The name for a double-null-terminated string would be szzDeletePath.)

    My psychic powers tell me that szDeletePath is not double-null-terminated.

    So far, my psychic powers haven't failed.

    Now, you might say that the fact that people make this mistake so often is a sign that the function is flawed. And if the function were designed today, I would agree with you. But this function in its public form is over fifteen years old (and in its private form, is around 20 years old), and back in those days, programmers were assumed to have the time to understand the subtleties of what they were doing.

  • The Old New Thing

    Microwave popcorn enthusiast proudly proclaims, "I *am* popcorn!"


    Oscar Night is a few weeks away, but when you settle in to watch the show with your bowl of popcorn, please be aware that inhaling deeply from the fumes of a freshly-opened bag of microwave popcorn is not the greatest decision you can make from a health standpoint. (Then again, you probably ought to reconsider eating microwave popcorn in the first place, but let's leave that aside.)

    A disease informally known as popcorn lung afflicts people who work in popcorn factories and has been known since 2002. But in 2007, doctor diagnosed the first case of popcorn lung in an end-user. The risk is not from eating the popcorn but from breathing it. "This patient described enjoying the smell so much he was actually inhaling the steam." The best part of the article was when the patient was asked, "Are you around a lot of popcorn?"

    The response: "I am popcorn."

    Update: Oscar Night is not actually this weekend. That's what happens when you schedule your blog entries over a year in advance and have to guess when Oscar Night is coming.

  • The Old New Thing












  • The Old New Thing

    Why can't I use the linker to delay-load a function from kernel32?


    For some time (I am too lazy to look up when it was introduced), the Visual Studio linker has supported a feature known as delay-loading. But why can't you use this feature to delay-load a function from kernel32? It would be very handy: If you write

    if (CurrentWindowsVersionSupportsKernelFunctionXyz())

    the program fails to load on versions of Windows which do not support the function Xyz because the Win32 load rejects loading a module that contains unresolved references. 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 LoadLibrary, and finding the target function means calling GetProcAddress, 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.

Page 1 of 4 (35 items) 1234