July, 2011

  • The Old New Thing

    Hey, let's report errors only when nothing is at stake!

    • 37 Comments

    Only an idiot would have parameter validation, and only an idiot would not have it. In an attempt to resolve this paradox, commenter Gabe suggested, "When running for your QA department, it should crash immediately; when running for your customer, it should silently keep going." A similar opinion was expressed by commenter Koro and some others.

    This replaces one paradox with another. Under the new regime, your program reports errors only when nothing is at stake. "Report problems when running in test mode, and ignore problems when running on live data." Isn't this backwards? Shouldn't we be more sensitive to problems with live data than problems with test data? Who cares if test data gets corrupted? That's why it's test data. But live data—we should get really concerned when there's a problem with live data. Allowing execution to continue means that you're attempting to reason about a total breakdown of normal functioning.

    Now, if your program is mission-critical, you probably have some recovery code that attempts to reset your data structures to a "last known good" state or which attempts to salvage what information it can, like how those space probes have a safe mode. And that's great. But silently ignoring the condition means that your program is going to skip happily along, unaware that what it's doing is probably taking a bad situation and subtly making it slightly worse. Eventually, things will get so bad that something catastrophic happens, and when you go to debug the catastrophic failure, you'll have no idea how it got that way.

  • The Old New Thing

    Simulating input via WM_CHAR messages may fake out the recipient but it won't fake out the input system

    • 19 Comments

    We saw some time ago that you can't simulate keyboard input with Post­Message. You may get away with it, depending on how the application you're trying to fake out processes input, but since you're just faking data, the application may discover that it's all a ruse when they try to access information that you didn't fake out, say by calling Get­Key­State and discovering that the key it was told was being pressed is in fact not being pressed after all.

    When you try to do this fake-out, you might or might not be able to fake out the application, but you're definitely not going to fake out the input system itself.

    I wrote a test program that simulates input via WM_CHAR messages to write characters into Notepad. I set the local screen saver timeout to one minute, but the screen saver launches after one minute even if my program has been running. On the other hand, when I type on the physical keyboard, the screen saver does not kick in, as expected. How does the screen saver know that the system is really idle and the input is just being generated by a program?

    Actually, the question is backwards. How does the screen saver find out about your fake input? Pumping WM_CHAR messages directly into Notepad bypasses the input system. It's not that it "knew" that you pulled an end run around it; it is merely acting on what it knows, and your fake input is not part of what it knows.

    It's like prank-calling somebody and saying, "Hi, this is your credit card company. You don't need to pay your bill this month. Don't worry about it." Sure, that person may be fooled into not paying their bill, but a month later, they're going to get a late payment notice from the credit card company. "How does the credit card company know that the bill wasn't forgiven and the phone call was fake?"

    If you want to generate input programmatically, use Send­Input.

    Note that the window manager still knows whether the input came from hardware or from Send­Input, and the SPI_SET­BLOCK­SEND­INPUT­RESETS system parameter controls whether programmatically-generated input should reset the screen saver.

  • The Old New Thing

    Luxurifying the camping experience in a different direction

    • 14 Comments

    Some time ago, I noted the increasing luxurification of camping, where people with more money than sense decide to go through the camping experience without building any of the character that comes with it.

    But that's not the only direction luxurification has moved. Unwilling to accept that "getting there is half the fun", some people take chartered planes to and from summer camp. Stick it out for the punch line in the final sentence of the article.

  • The Old New Thing

    You don't need to ask me a question the compiler can answer more accurately

    • 11 Comments

    A customer reported having problems developing the correct p/invoke signature for the INPUT structure. "The code works on 32-bit machines, but on 64-bit machines, the call to Send­Input returns ERROR_INVALID_PARAMETER." The sample code included the lines

    INPUT[] inputs = new INPUT[1];
    inputs[0].type = INPUT_KEYBOARD;
    ...
    int size = Marshal.SizeOf(inputs[0]);
    if (!SendInput(1, ref inputs, size))
    {
     Console.WriteLine("Failed with error {0}", GetLastError());
    }
    

    My response was simply, "I bet you are passing the wrong structure size. Note that on 64-bit Windows, the alignment of the inner structures is 8-byte rather than 4-byte. Write a C++ program that does the same thing and compare."

    The customer decided to read only the first sentence of my reply, possibly the second, and ignore the third. "So what size should the INPUT structure be on 64-bit machines?"

    There are a few ways you can go about finding out the size of a structure.

    One method is to ask Raymond. This may work, but it makes Raymond rather grumpy since it demonstrates that you are lazy and just want the answer handed to you with no effort (or demonstration of understanding) on your part.

    Another method is to sit down with the structure definition and work out the size on a piece of paper, bearing in mind that various #pragma pack directives can change how the structure packing is applied.

    But the easiest way to get the size of a structure is to use the sizeof operator, because that is after all the method the compiler uses. To get the same value as the C++ compiler, just ask the C++ compiler!

    #include <windows.h>
    #include <stdio.h>
    #include <stddef.h>
    
    int __cdecl main(int argc, char **argv)
    {
     printf("the size is %d\n", sizeof(INPUT));
     printf("the offset of mi is %d\n", offsetof(INPUT, mi));
     return 0;
    }
    

    You can now use this amazing technique to answer wolf550e's question:

    SSE types require 16-byte alignment. I guess they aren't used in the Windows SDK? How about directX or something where they are used?
  • The Old New Thing

    No, we're not going to play Stairway to Heaven, and please tell everbody else in your area code to stop calling me

    • 26 Comments

    Some time ago, I told the story of how one employee's phone received calls intended for a local radio station's contest line due to people dialing seven digits instead of ten and defaulting to the wrong area code.

    Upon reading that story, a colleague of mine pointed out that one of the conference rooms in his building has a similar problem. The direct line for the conference room is identical to the request line for a local radio station, save for the area code. People who work in the building know never to answer the phone in that conference room.

    (Although apparently there have been a couple of pranks involving the call-forwarding function on the conference room telephone.)

  • The Old New Thing

    How is it possible to run Wordpad by just typing its name even though it isn't on the PATH?

    • 29 Comments

    In a comment completely unrelated to the topic, Chris Capel asks how Wordpad manages to run when you type its name into the Run dialog even though the command prompt can't find it? In other words, the Run dialog manages to find Wordpad even though it's not on the PATH.

    Chris was unable to find anywhere I discussed this issue earlier, but it's there, just with Internet Explorer as the application instead of Wordpad.

    It's through the magic of App Paths.

    App Paths was introduced in Windows 95 to address the path pollution problem. Prior to the introduction of App Paths, typing the name of a program without a fully-qualified path resulted in a search along the path, and if it wasn't found, then that was the end of that. File not found. As a result, it became common practice for programs, as part of their installation, to edit the user's AUTOEXEC.BAT and add the application's installation directory to the path.

    This had a few problems.

    First of all, editing AUTOEXEC.BAT is decidedlly nontrivial since batch files can have control flow logic like IF and CALL and GOTO. Finding the right SET PATH=... or PATH ... command is an exercise in code coverage analysis, especially since MS-DOS 6 added multi-config support to CONFIG.SYS, so the value of the CONFIG environment variable is determined at runtime. If you wanted to avoid hanging your setup program, you would have to solve the Halting Problem. (You can't just stick at PATH ... at the beginning because it might get wiped out by a later PATH command, and you can't just stick it at the end, because control might never reach last line of the batch file.)

    And of course, very few uninstall programs would take the time to undo the edits the installer performed, and even if they tried, there's no guarantee that the undo would be successful, since the user (or another installer!) may have edited the AUTOEXEC.BAT file in the meantime.

    Even if you postulate the existence of the AUTOEXEC.BAT editing fairy who magically edits your AUTOEXEC.BAT for you, you still run into the PATH length limit. The maximum length of a command line was 128 characters in MS-DOS, and if each program added itself to the PATH, it wouldn't be long before the PATH reached its maximum length.

    Pre-emptive Yuhong Bao irrelevant detail that has no effect on the story: Windows 95 increased the maximum command line length, but the program being launched needed to know where to look for the "long command line". And that didn't help existing installers which were written against the old 128-character limit. Give them an AUTOEXEC.BAT with a line longer than 128 characters and you had a good chance that you'd hit a buffer overflow bug.

    On top of the difficulty of adding more directories to the PATH, there was the recognition that this was another case of using a global setting to solve a local problem. It seemed wasteful to add a directory to the path just so you could find one file. Each additional directory on the path slowed down path sarching operations, even the ones unrelated to locating that one program.

    Enter App Paths. The idea here is that instead of adding your application directory to the path, you just create an entry under the App Paths key saying, "If somebody is looking to execute contoso.exe, I put it over here." Instead of adding an entire directory to the path, you just add a single file, and it's used only for application execution purposes, so it doesn't slow down other path search operations like loading DLLs.

    (Note that the old documentation on App Paths has been superseded by the new documentation linked above.)

    Now that there was a place to store information associated with a particular application, you may as well use it for other stuff as well. A secondary source of path pollution came from applications which added not only the application directory to the path, but also a helper directory where the application kept its DLLs. To address this, an additional Path value specified which directories your application wanted to be added to the path before it was executed. Over time, additional attributes were added to the App Paths key, such as the UseUrl value we saw some time ago.

    When you type the name of a program into the Run dialog (with no path), the Shell­Execute function checks if the name corresponds to an application registered under App Paths. If so, then it uses the registration information to launch the application. Hooray, applications can be run by just typing their name without requiring them to modify the global path.

    Note that this extra lookup is performed only by the Sh­ellExecute family of functions, so if you use Create­Process or Search­Path, you'll still get ERROR_FILE_NOT_FOUND.

    Now, the intent was that the registered full path to the application is the same as the registered short name, just with a full path in front. For example, wordpad.exe registers the full path of %ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE. But there's no check that the two file names match. The Pbrush folks took advantage of this by registering an application path entry for pbrush.exe with a full path of %SystemRoot%\System32\mspaint.exe: That way, when somebody types pbrush into the Run dialog, they get redirected to mspaint.exe.

    Sneaky.

  • The Old New Thing

    How do I disable windowless control support in dialog boxes?

    • 12 Comments

    A customer wanted to know how to disable windowless control support in dialog boxes. "The customer has a CommandButton ActiveX control on his dialog box, and using Get­Dlg­Item to get the window handle of the command button succeeded with VC 6.0, but when compiled with VC 9.0, it does not create a window. I'm guessing that this is caused by Dialog­Box's support for windowless controls. Is it possible to disable support for windowless controls?"

    The question on its face is somewhat puzzling, because dialog boxes don't "support" or "not support" windowless controls. It's like asking, "I want rice that doesn't support meat. My customer is a vegetarian and cannot eat meat." Rice doesn't support meat, and it doesn't not-support meat. If you don't want meat, then don't add meat. And if you don't want windowless controls on your dialog box, then don't create windowless controls.

    I was also not sure what the customer meant by CommandButton, because Win32 command buttons are not ActiveX controls. The customer must be referring to something else also called Command­Button, in which case the customer should also consult the documentation for that something else to see if there's a way to control its windowed/windowless behavior.

    The customer liaison gave some more details: "My customer uses Get­Dlg­Item to get the handle of a specific window. This method worked in VC 6.0 since VC 6.0 doesn't support windowless controls. But VC 9.0 added support for windowless controls in dialog boxes, which breaks my customer's code. Is there a way to disable support for windowless controls in dialog boxes?"

    It took a few more questions, but eventually we figured out that the customer was not using raw Win32 dialog boxes (as Dialog­Box suggested in the original question) but rather MFC dialog boxes, and the CommandButton in question is a Microsoft Forms 2.0 CommandButton control.

    "The customer simply wants to continue using his code without modification. He is already using the Microsoft Forms 2.0 CommandButton control, and he is already using Get­Dlg­Item to obtain its handle, but that technique no longer works."

    The pieces started to fall into place, and somebody from the Visual Studio team provided an explanation: The version of MFC which comes with Visual Studio 2000 added support for hosting windowless ActiveX controls. By default, the MFC hosting code permits controls to be added as windowless controls if the control requests it. To force all controls to be windowed, you need to provide a custom class which derives from COle­Control­Site and overrides IOle­In­Place­Site­Windowless::Can­Windowless­Activate to return S_FALSE. Then override the dialog's CWnd::Create­Control­Site method to return an instance of this class instead of the default control site.

    I haven't actually tested this to see if it works, but the customer didn't come back, so either it worked, or they decided that we were jerks and didn't want to waste their time with us any more.

  • The Old New Thing

    Why is secur32.dll called secur32.dll and not secure32.dll?

    • 22 Comments

    Many years ago, in a discussion of why you shouldn't name your DLL "security.dll", I dug a bit into the history behind the DLL. Here are some other useless tidbits about that file.

    Originally, there were two DLLs called security.dll. One was the 32-bit version and one was the 16-bit version. They could coexist because the 32-bit version was in the system32 directory and the 16-bit version was in the system directory.

    And then Windows 95 showed up and screwed up everything.

    Windows 95 did not have separate system32 and system directories. All the system files, both 16-bit and 32-bit, were lumped together in a single system directory. When the Security Support Provider Interface was ported to Windows 95, this created a problem, for it would require two files in the same directory to have the same name. Since the 16-bit version had seniority (because your Windows 95 installation may have been an upgrade install over Windows 3.1, which would have been the 16-bit version), it was the 32-bit version that had to be renamed.

    Okay, so why secur32.dll? Well, security32.dll was too long, since it exceeded the classic 8.3 limit, and Windows NT supported being run on FAT volumes (which necessarily did not support long file names, since long file names on FAT didn't exist until Windows 95). Okay, but why secur32.dll instead of secure32.dll, which still fits inside the 8.3 constraints?

    Nobody knows for sure any more; the person who chose the name left Microsoft a long time ago. Perhaps because secur32.dll looked better than securi32.dll. Maybe he couldn't count.

  • The Old New Thing

    How do I find the original name of a hard link?

    • 35 Comments

    A customer asked, "Given a hardlink name, is it possible to get the original file name used to create it in the first place?"

    Recall that hard links create an alternate name for a file. Once that alternate name is created, there is no way to tell which is the original name and which is the new name. The new file does not have a "link back to the original"; they are both links to the underlying file content. This is an old topic, so I won't go into further detail. Though this question does illustrate that many people continue to misunderstand what hard links are.

    Anyway, once you figure out what the customer is actually asking, you can give a meaningful answer: "Given the path to a file, how can I get all the names by which the file can be accessed?" The answer is Find­First­File­NameW.

    Note that the names returned by the Find­First­File­NameW family of functions are relative to the volume mount point. To convert it to a full path, you need to append it to the mount point. Something like this:

    typedef void (*ENUMERATEDNAMEPROC)(__in PCWSTR);
    
    void ProcessOneName(
        __in PCWSTR pszVolumeRoot,
        __in PCWSTR pszLink,
        __in ENUMERATEDNAMEPROC pfnCallback)
    {
      wchar_t szFile[MAX_PATH];
      if (SUCCEEDED(StringCchCopy(szFile, ARRAYSIZE(szFile), pszVolumeRoot)) &&
          PathAppend(szFile, pszLink)) {
       pfnCallback(szFile);
      }
    }
    
    void EnumerateAllNames(
        __in PCWSTR pszFileName,
        __in ENUMERATEDNAMEPROC pfnCallback)
    {
     // Supporting paths longer than MAX_PATH left as an exercise
     wchar_t szVolumeRoot[MAX_PATH];
     if (GetVolumePathName(pszFileName, szVolumeRoot, ARRAYSIZE(szVolumeRoot))) {
      wchar_t szLink[MAX_PATH];
      DWORD cchLink = ARRAYSIZE(szLink);
      HANDLE hFind = FindFirstFileNameW(pszFileName, 0, &cchLink, szLink);
      if (hFind != INVALID_HANDLE_VALUE) {
       ProcessOneName(szVolumeRoot, szLink, pfnCallback);
       while (cchLink = ARRAYSIZE(szLink),
              FindNextFileNameW(hFind, &cchLink, szLink)) {
        ProcessOneName(szVolumeRoot, szLink, pfnCallback);
       }
       FindClose(hFind);
      }
     }
    }
    
    // for demonstration purposes, we just print the name
    void PrintEachFoundName(__in PCWSTR pszFile)
    {
     _putws(pszFile);
    }
    
    int __cdecl wmain(int argc, wchar_t **argv)
    {
     for (int i = 1; i < argc; i++) {
      EnumerateAllNames(argv[i], PrintEachFoundName);
     }
     return 0;
    }
    

    Update: Minor errors corrected, as noted by acq and Adrian.

  • The Old New Thing

    Some mailing lists come with a negative service level agreement, but that's okay, because everybody is in on the joke

    • 14 Comments

    As I noted some time ago, there's a mailing list devoted to chatting among people who work in a particular cluster of buildings. It's not a technical support mailing list, but people will often ask a technical question on the off chance that somebody can help, in the same way that you might ask your friends for some help with something.

    Of course, one consequence of this is that the quality of the responses is highly variable. While there's a good chance that somebody will help you with your problem, there's also a good chance that a technical question will receive a highly unhelpful response just for fun, in the same way your friend might respond to a question with a funny but unhelpful answer. (And there's also a good chance that a technical question will get both types of replies.) You don't complain about this because, well, that's what you sort of expect when you use this mailing list.

    An illustration of this principle comes from the following thread:

    When I do ABC, I get XYZ. How do I get DEF?

    A short while later, the same person replied to his own question.

    Nevermind. I found the right mailing list to ask this question.

    That didn't stop somebody from responding:

    This mailing list is the correct place to send all questions. You have to use a different mailing list to get answers, though.
Page 1 of 3 (26 items) 123