• The Old New Thing

    When was the WM_COPYDATA message introduced, and was it ported downlevel?

    • 9 Comments

    Gabe wondered when the WM_COPY­DATA message was introduced.

    The WM_COPY­DATA message was introduced by Win32. It did not exist in 16-bit Windows.

    But it was there all along.

    The The WM_COPY­DATA message was carefully designed so that it worked in 16-bit Windows automatically. In other words, you retained your source code compatibility between 16-bit and 32-bit Windows without having to do a single thing. Phew, one fewer breaking change between 16-bit and 32-bit Windows.

    As Neil noted, there's nothing stopping you from sending message 0x004A in 16-bit Windows with a window handle in the wParam and a pointer to a COPY­DATA­STRUCT in the lParam. Since all 16-bit applications ran in the same address space, the null marshaller successfully marshals the data between the two processes.

    In a sense, support for the WM_COPY­DATA message was ported downlevel even before the message existed!

  • The Old New Thing

    How can you use both versions 5 and 6 of the common controls within the same module?

    • 9 Comments

    Commenter Medinoc was baffled by the statement that the decision to use the visual-styles-enabled version of the common controls library is done on a window-by-window basis. " Isn't it rather on a per-module basis, depending on each module's manifest? If it is indeed on a per-window basis, how does one choose?"

    Whether a particular call to Create­Window (or one of its moral equivalents) gets the classic version of the control or the visual-styles-enabled version of the control depends on which activation context is active at the point of the call. If an activation context with version 6 of the common controls is active, then you get the control from version 6 of the common controls. Otherwise, you get the classic control.

    If you use the ISOLATION_AWARE_ENABLED macro, then including commctrl.h turns on a bunch of macros that take all your calls to Create­Window and related functions, and converts them into something like this:

    HWND CreateWindow_wrapped(... parameters ...)
    {
     HWND hwnd = nullptr;
     ULONG_PTR ulCookie;
     if (ActivateActCtx(ModuleContext, &ulCookie)) {
      hwnd = CreateWindow(... parameters ...);
     DeactivateActCtx(0, ulCookie);
     }
     return hwnd;
    }
    

    where Module­Context is a global variable that holds the activation context you specified in your manifest.

    In other words, any time your code tries to create a window, the wrapper macros activate your v6 manifest, create the window, then deactivate the manifest.

    Remember that nobody walks the stack looking to see who the caller is. The return address is not reliable. (And checking the return address doesn't help for dynamically-generated code anyway.) The way to know which activation context is active is for somebody to actually come out and set it.

    Back to the question: The way you choose whether you want a classic or a visual-styles-enabled version of a control is by deciding whether or not to have the v6 manifest active when you call Create­Window.

    A common mistake is that people will call a function that requires a v6 manifest, such as Task­Dialog, but they will forget to activate the v6 manifest before calling. The result is that they call into version 6 of the common controls, but when the common controls library tries to create its task dialog, it fails because the v5 context is active, and the v5 context does not have a task dialog control.

  • The Old New Thing

    Why does saving a file in Notepad fire multiple FindFirstChangeNotification events?

    • 9 Comments

    Many people have noticed that the Read­Directory­ChangesW and Find­First­Change­Notification functions (and therefore their BCL equivalent File­System­Watcher and WinRT equivalent Storage­Folder­Query­Result) fire multiple FILE_ACTION_MODIFIED events when you save a file in Notepad. Why is that?

    Because multiple things were modified.

    Notepad opens the file for writing, writes the new data, calls Set­End­Of­File to truncate any excess data (in case the new file is shorter than the old file), then closes the handle. Two things definitely changed, and a third thing might have changed.

    • The file last-modified time definitely changed.
    • The file size definitely changed.
    • The file last-access time might have changed.

    It's therefore not surprising that you got two events, possibly three.

    Remember the original design goals of the Read­Directory­ChangesW function: It's for letting an application cache a directory listing and update it incrementally. Given these design goals, filtering out redundant notifications in the kernel is not required aside from the performance benefits of reduced chatter. In theory, Read­Directory­ChangesW could report a spurious change every 5 seconds, and the target audience for the function would still function correctly (albeit suboptimally).

    Given this intended usage pattern, any consumer of Read­Directory­ChangesW needs to accept that any notifications you receive encompass the minimum information you require in order to keep your cached directory information up to date, but it can contain extra information, too. If you want to respond only to actual changes, you need to compare the new file attributes against the old ones.

    Bonus chatter: Actually, the two things that changed when Notepad set the file size are the allocation size and the file size (which you can think of as the physical and logical file sizes, respectively). Internally, this is done by two separate calls into the I/O manager, so it generates two change notifications.

  • The Old New Thing

    Letting the boss think your project is classier than it really is

    • 16 Comments

    Once upon a time, there was a team developing two versions of a product, the first a short-term project to ship soon, and the other a more ambitious project to ship later. (Sound familiar?) They chose to assign the projects code names Ren and Stimpy, in honor of the lead characters from the eponymous cartoon series.

    Over time, the two projects merged, and the code name that stuck was Ren.

    When the project came up in a meeting with Bill Gates, it was mentioned verbally but never spelled out, and since Bill wasn't closely tuned into popular culture, he mapped the sound /rɛn/ not to the hairless Mexican dog but to the Christopher Wren, architect of St. Paul's Cathedral. In follow-up email, he consistently referred to the project by the name "Wren".

    The Ren team liked the fact that their name gave the boss the impression that the project was going to be a masterpiece of architectural beauty, so they never told him he got the name wrong.

    Even though it has nothing to do with the story: The project in question is the one that eventually became known to the world as Outlook.

  • The Old New Thing

    Warehouse holding 1000 cans of surströmming burns to the ground, insert punch line here

    • 12 Comments

    A warehouse in Sweden holding 1000 cans of surströmming burned to the ground last week. No people were injured.

    The cans of surströmming, already prone to violent decompression under normal conditions, exploded over a period of six hours. Some of them turned into projectiles and shot through the air.

    No information on what effect this will have on the supply of surströmming ice cream. I knew you were wondering.

  • The Old New Thing

    Getting the location of the Close button in the title bar

    • 19 Comments

    Today's Little Program locates the × button in the corner of the window and, just to show that it found it, displays a balloon tip pointing at it.

    Let's start with the program from last week, the one that displays a balloon tip, then make these changes:

    BOOL GetCloseButtonCenter(HWND hwnd, POINT *ppt)
    {
     TITLEBARINFOEX info = { sizeof(info) };
     if (!SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info))
        return FALSE;
     if (info.rgstate[5] & (STATE_SYSTEM_INVISIBLE |
                                STATE_SYSTEM_OFFSCREEN |
                                STATE_SYSTEM_UNAVAILABLE)) return FALSE;
     ppt->x = info.rgrect[5].left +
                 (info.rgrect[5].right - info.rgrect[5].left) / 2;
     ppt->y = info.rgrect[5].top +
                 (info.rgrect[5].bottom - info.rgrect[5].top) / 2;
     return TRUE;
    }
    
      case TEXT(' '):
        if (GetCloseButtonCenter(hwnd, &pt)) {
          SendMessage(g_hwndTT, TTM_TRACKPOSITION, 0, MAKELPARAM(pt.x, pt.y));
    

    Instead of positioning the balloon at the cursor position, we put it at the center of the Close button. We use the WM_GET­TITLE­BAR­INFO­EX message to obtain information about the window title bar, specifically checking information about the Close button. After verifying that it is visible and on-screen and enabled, we calculate its center point and return success.

    The WM_GET­TITLE­BAR­INFO­EX message is new for Windows Vista. Next time, we'll cook up a method that works on Windows 2000 and Windows XP.

  • The Old New Thing

    If a lot of settings are ignored when set in a DLL, why bother even letting you set them on a DLL?

    • 34 Comments

    There are many settings that are ignored when set in a DLL. /LARGE­ADDRESS­AWARE. Size­Of­Stack­Reserve and Size­Of­Stack­Commit. There are plenty of others. Commenter 640k asks why these settings even exist for DLLs if they has no effect.

    Because they are settings for PE modules in general.

    If there were separate file formats for EXEs and DLLs, then there would have to be two different module loaders, one for EXEs and one for DLLs. This creates extra work for no particular benefit aside from satisfying some anal-retentive compulsion that nothing be wasted. As far as I can tell, all operating systems use a common file format for both executables and libraries.

    If it really bothers you, you can consider this flags as Reserved for future use when applied to DLLs.

  • The Old New Thing

    How do I extract an icon at a nonstandard size if IExtractIcon::Extract tells me to go jump in a lake?

    • 13 Comments

    Commenter Ivo notes that if you ask IExtract­Icon::Extract to extract an icon at a particular size, the function can return S_FALSE which means "Go jump in a lake do it yourself." But how can you do it yourself? The Extract­Icon and Extract­Icon­Ex functions don't let you specify a custom size, and Load­Image doesn't work with icon indices (only resource IDs).

    The SH­Def­Extract­Icon function comes to the rescue. This takes all the parameters of IExtract­Icon::Extract (plus a bonus flags parameter), and it will actually do an extraction.

    Let's extract an icon from Explorer at 48×48, just for illustration. As usual, start with our scratch program, then make these changes:

    #include <shlobj.h>
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
      HICON hico;
      if (SHDefExtractIcon(TEXT("C:\\Windows\\Explorer.exe"),
                           1, 0, &hico, NULL, 48) == S_OK) {
        DrawIconEx(pps->hdc, 0, 0, hico, 0, 0, 0, NULL, DI_NORMAL);
        DestroyIcon(hico);
      }
    }
    

    Run the program, and observe that it draws the second icon from Explorer (whatever it is) at a size of 48×48.

  • The Old New Thing

    The StartRunNoHOMEPATH policy affects whether the user's HOME path is the current directory when you use Start.Run, but make sure you read that carefully

    • 15 Comments

    A customer (via the customer liaison) discovered that even though they had set the Start­Run­No­HOME­PATH policy, they found "if the user creates a Notepad file, the file is searched in the user's HOME directory, in contradiction of Start­Run­No­HOME­PATH policy,"

    I asked the liaison to confirm: "The steps you describe are rather vague. Are you saying that the problem occurs when the user opens the Run dialog and types Notepad filename.txt?"

    The customer liaison replied, "I believe the scenario is close to what you describe. The user opens the Run dialog, types Notepad, then types some text into Notepad and then does a Save As. I will confirm with the customer."

    A few days later (the customer was on leave), the customer liaison provided the exact steps:

    • Open the Start menu (Windows 7)
    • Type Notepad to search the Start menu.
    • When the Notepad program is found, click on it.
    • Type some text.
    • Perform a Save As. This operation is slow. Network traces show many accesses to the user's HOME directory.

    Okay, well, now that the steps are all carefully spelled out, it is clear what is going on. Or more accurately, it is clear what is not going on.

    The Start­Run­No­HOME­PATH policy controls the working directory when a program is run from the Start.Run dialog. Like it says in the KB article:

    Symptoms

    If you have a home folder set and you try to run a program by clicking Start and then clicking Run, Windows searches your home folder for the program before searching the path.

    The article then goes on to describe how you can solve the problem if those are the symptoms you are trying to relieve.

    But those symptoms do not match the customer's problem.

    The customer ran the program directly from the Start menu, not by going through the Start.Run dialog. Therefore, the KB article and the Start­Run­No­HOME­PATH policy do not apply.

    Policies do what they are documented to do, not what you wish they did.

  • The Old New Thing

    Why does the common file save dialog create a temporary file and then delete it?

    • 29 Comments

    When you call GetSaveFileName, the common file save dialog will ask the user to choose a file name, and just before it returns it does a little create/delete dance where it creates the file the user entered, and then deletes it. What's up with that?

    This is a leftover from the ancient days of 16-bit Windows 3.1, back when file systems were real file systems and didn't have this namby-pamby "long file name" or "security" nonsense. (Insert sound effect of muscle flexing and gutteral grunting.)

    Back in those days, the file system interface was MS-DOS, and MS-DOS didn't have a way to query security attributes because, well, the file systems of the day didn't have security attributes to query in the first place.

    But network servers did.

    If you mapped a network drive from a server running one of those fancy new file systems, then you were in this case where your computer didn't know anything about file system security, but the server did. The only way to find out whether you had permission to create a file in a directory was to try it and see whether it worked or whether it failed with the error ERROR_ACCESS_DENIED (or, as it was called back in the MS-DOS days, "5"),

    Another reason why a server might reject a file name was that it contained a character that, while legal in Windows, was not legal on the server. At the time, the most common reason for this was that you used a so-called "extended character" (in other words, a character outside the ASCII range like an accented lowercase e) which was part of your local code page but not on the server's.

    Yet another possibility was that the file name you chose would exceed the server's path name limit. For example, suppose the server is running Windows for Workgroups (which has a 64-character maximum path name limit), and it shared out C:\some\deep\directory\on\the\server as \\server\share. If you mapped M: to \\server\share, then the maximum path name on M: was only about 30 characters because C:\some\deep\directory\on\the\server used up half of your 64-character limit.

    The only way to tell whether the file could be created, then, was to try to create it and see what happens. After creating the test file (to see if it could), the common file save dialog immediately deleted it in order to cover its tracks. (This could lead to some weird behavior if users picked a directory where they had permission to create files but no permission to delete files that they created!)

    This "test to see if I can create the file by creating it" behavior has been carried forward ever since, but you can suppress it by passing the OFN_NOTESTFILECREATE flag.

Page 7 of 426 (4,260 items) «56789»