• The Old New Thing

    The dialog manager, part 1: Warm-ups

    • 21 Comments
    I think a lot of confusion about the dialog manager stems from not really understanding how it works. It's really not that bad. I'll start by describing how dialog boxes are created over the next few articles, then move on to the dialog message loop, and wrap up with some topics regarding navigation. There will be nine parts in all.

    The first major chunk of the dialog manager has to do with reading the dialog template and creating a dialog box based on it.

    All of the CreateDialogXxx functions are just front-ends to the real work that happens in CreateDialogIndirectParam. Some of them are already visible in the macros: CreateDialog is just a wrapper around CreateDialogParam, with a parameter of zero. Similarly, CreateDialogIndirect is just a wrapper around CreateDialogIndirectParam with a zero parameter.

    Here's a slightly less trivial wrapper:

    HWND WINAPI CreateDialogParam(HINSTANCE hinst,
        LPCTSTR pszTemplate, HWND hwndParent,
        DLGPROC lpDlgProc, LPARAM dwInitParam)
    {
      HWND hdlg = NULL;
      HRSRC hrsrc = FindResource(hinst, pszTemplate,
                                 RT_DIALOG);
      if (hrsrc) {
        HGLOBAL hglob = LoadResource(hinst, hrsrc);
        if (hglob) {
          LPVOID pTemplate = LockResource(hglob); // fixed 1pm
          if (pTemplate) {
            hdlg = CreateDialogIndirectParam(hinst,
                     pTemplate, hwndParent, lpDlgProc,
                     dwInitParam);
          }
          FreeResource(hglob);
        }
      }
      return hdlg;
    }
    

    All CreateDialogParam does is use the hinst/pszTemplate to locate the lpTemplate, then use that template in CreateDialogIndirectParam.

    Okay, this was easy. Tomorrow, we're going to create the dialog from the template.

  • The Old New Thing

    When a program asks you a question and then gets upset if you answer it

    • 33 Comments

    JeffDav's story of a program that didn't like it when he told it where to install reminded me of another program that we dealt with during Windows 95 development.

    This was a big-name program developed by one of the biggest-of-the-big name software companies. Let's give this program the imaginary name "LitWare". Its setup program asked you where you wanted the program to be installed, and it suggested "C:\LITWARE". If you accepted the default, then everything proceeded normally.

    However, if you changed the default to anything else, the setup program ran to completion, but the program itself wouldn't run.

    Because the program contained the hard-coded path "C:\LITWARE" and insisted that it find its support files in that directory.

    At least Jeff's program realized that it was about to be installed into a directory where it would fail to work!

  • The Old New Thing

    The CEO-to-English Phrase Book, a continuing series from Slate

    • 10 Comments

    I'm an economics geek, so of course I'm a fan of Slate's Moneybox column as well as The Dismal Science and most of all, Steven Landsburg's gleefully provocative Everyday Economics. I'm also a language geek, so I've been quite enjoying the occasional Moneybox articles which decode CEO speak into plain English titled The CEO-English Phrase Book, thereby combine two geek topics into one.

    Terms that have been defined so far:

  • The Old New Thing

    If you disable drag/drop on the Start menu, you also disable right-click

    • 26 Comments

    This is one of those poorly-worded options.

    In the Start menu configuration dialog, you can choose to uncheck "Enable dragging and dropping". This setting disables drag/drop but also disables right-click context menus. The connection between the two is explained in the Group Policy Editor, but is unfortunately oversimplified in the general-public configuration dialog.

    Why does disabling dragging and dropping also disable context menus?

    History, of course.

    Originally, the "Disable drag/drop on the Start menu" setting was a system policy, intended to be set by corporate IT departments to prevent their employees from damaging the Start menu. With this setting, users could no longer drag items around to rearrange or reorganize their Start menu items. This is a good thing in corporate environments because it reduces support calls.

    But very quickly, the IT departments found a loophole in this policy: You could right-click an item on the Start menu and select Cut, Copy, Paste, Delete, or Sort by Name, thereby giving you access to the operations that the policy was trying to block. Therefore, they requested that the scope of the policy be expanded so that it also disabled the context menu.

    In Windows XP, it was decided to expose what used to be an advanced deployment setting to the primary UI, and so it was. Since it's the same setting, it carried the loophole-closure with it.

  • The Old New Thing

    The invisible price reduction

    • 17 Comments

    Swedish discount warehouse chain Coop Forum is running an advertising campaign claiming "New Lower Prices!", but how can you tell? Apparently you're not supposed to (shocking!) compare current prices against what they were before the ad campaign.

    Raymond's bad translation follows.

    Coops "new" price reduction not new

    A major new price reduction advertising campaign issued by discount warehouse chain Coop Forum this week to all households in the Stockholm area could be misleading.

    The list of which merchandise which actually had their prices lowered, that is secret, according to Roger Gehrman, vice managing director for Coop Forum.

    "The price structure is one of the probably most important trade secrets we have," he says.

    But you claim that you are lowering prices. How can one be sure that you really are doing it?

    "You can't compare today's prices with how they were earlier," says Roger Gehrman.

    Difficult to know for both Ica and Coop

    It is almost just as hard to get a grasp on Coop's claimed price reduction as it is with Ica's.

    Ekot's check shows that both chains refuse to show which individual items were lowered or how large individual price reductions are.

    Don't want to report which items were reduced

    Coop's manager Roger Gehrman doesn't even want to state which product categories were reduced the most.

    "I don't want to go into that. It is nearly all of our product range but I don't have detailed knowledge to talk about a specifically reduced product and I don't have the list in front of me either," he says.

    This week's advertising flyer from Coop Forum to households in the Stockholm region are misleading. "Welcome to our largest price reduction ever" and "New lower prices" says the discount warehouse chain now about a price reduction which in the Stockholm area was already put into place last autumn.

    "It happened at the end of October 2004," says the Coop Forum manager.

    One figure Coop management did release, and that's how much the 7,000 items were reduced on average: five percent.

  • The Old New Thing

    Pointers to virtual functions with adjustors

    • 8 Comments

    As a mental exercise, let's combine two mind-numbing facts about pointers to member functions, namely that all pointers to virtual functions look the same and that pointers to member functions are very strange animals. The result may make your head explode.

    Consider:

    class Class1 {
     public: virtual int f() { return 1; }
    };
    
    class Class2 {
     public: virtual int g() { return 2; }
    };
    
    class Class3 : public Class1, public Class2 {
    };
    
    int (Class3::*pfn)() = Class3::g;
    

    Here, the variable pfn consists of a code pointer and an adjustor. The code pointer gives you the virtual call stub:

     mov eax, [ecx]             ; first vtable
     jmp dword ptr [eax]        ; first function
    

    and the adjustor is sizeof(Class1) (which in our case would be 4 on a 32-bit machine). The result, then, of compiling a function call (p->*pfn)() might look something like this:

     mov ecx, p
     lea eax, pfn
     add ecx, dword ptr [eax+4] ; adjust
     call dword ptr [eax]       ; call
    -- transfers to
     mov eax, [ecx]             ; first vtable
     jmp dword ptr [eax]        ; first function
    -- transfers to
     mov eax, 2                 ; return 2
     ret
    

    Okay, I lied. It's really not all that complicated after all. But you can probably still impress your friends with this knowledge. (If you have really geeky friends.)

  • The Old New Thing

    Why does the debugger show me the wrong virtual function?

    • 22 Comments

    Pointers to virtual functions all look basically the same and therefore, as we learned last time, all end up merged into a single function. Here's a contrived example:

    class Class1
    {
    public:
     virtual int f1() { return 0; }
     virtual int f2() { return 1; }
    };
    
    class Class2
    {
    public:
     virtual int g1() { return 2; }
     virtual int g2() { return 3; }
    };
    
    int (Class1::*pfn1)() = Class1::f2;
    int (Class2::*pfn2)() = Class2::g2;
    

    If you take a look at pfn1 and pfn2 you'll see that the point to the same function:

    0:000> dd pfn1 l1
    01002000  010010c8
    0:000> dd pfn2 l1
    01002004  010010c8
    0:000> u 10010c8 l2
    010010c8 8b01     mov     eax,[ecx]           ; first vtable
    010010ca ff6004   jmp     dword ptr [eax+0x4] ; second function
    

    That's because the virtual functions Class1::f2 and Class2::g2 are both stored in the same location relative to the respective object pointer: They are the second entry in the first vtable. Therefore, the code to call those functions is identical and consequently has been merged by the linker.

    Notice that the function pointers are not direct pointers to the concrete implementations of Class1::f2 and Class2::g2 because the function pointer might be applied to a derived class which override the virtual function:

    class Class3 : public Class1
    {
    public:
     virtual int f2() { return 9; }
    };
    
    Class3 c3;
    (c3.*pfn1)(); // calls Class3::f2
    

    Applying the function pointer invokes the function on the derived class, which is the whole point of declaring the function Class1::f2 as virtual in the first place.

    Note that the C++ language explicitly states that the result of comparing non-null pointers to virtual member functions is "unspecified", which is language-standards speak for "the result not only depends on the implementation, but the implementation isn't even required to document how it arrives at the result."

  • The Old New Thing

    Why does the debugger show me the wrong function?

    • 44 Comments

    Often you'll be minding your own business debugging some code, and you decide to step into one function and the debugger shows that you're in some other function. How did that happen?

    class Class1
    {
    public:
     int *GetQ() { return q; }
    private:
     int *p;
     int *q;
    };
    
    class Class2
    {
    public:
     virtual int GetValue() { return value; }
    private:
     int value;
    };
    

    You then step through code that does something like this:

    int Whatever(Class2 *p)
    {
     return p->GetValue();
    }
    

    And when you step into the call to p->GetValue() you find yourself in Class1::GetQ. What happened?

    What happened is that the Microsoft linker combined functions that are identical at the code generation level.

    ?GetQ@Class1@@QAEPAHXZ PROC NEAR    ; Class1::GetQ, COMDAT
      00000 8b 41 04         mov     eax, DWORD PTR [ecx+4]
      00003 c3               ret     0
    ?GetQ@Class1@@QAEPAHXZ ENDP         ; Class1::GetQ
    
    ?GetValue@Class2@@UAEHXZ PROC NEAR  ; Class2::GetValue, COMDAT
      00000 8b 41 04         mov     eax, DWORD PTR [ecx+4]
      00003 c3               ret     0
    ?GetValue@Class2@@UAEHXZ ENDP       ; Class2::GetValue
    

    Observe that at the object code level, the two functions are identical. (Note that whether two functions are identical at the object code level is highly dependent on which version of what compiler you're using, and with which optimization flags. Identical code generation for different functions occurs with very high frequency when you use templates.) Therefore, the linker says, "Well, what's the point of having two identical functions? I'll just keep one copy and use it to stand for both Class1::GetQ and Class2::GetValue."

    0:000> u Class1::GetQ
    010010d6 8b4104           mov     eax,[ecx+0x4]
    010010d9 c3               ret
    0:000> u Class2::GetValue
    010010d6 8b4104           mov     eax,[ecx+0x4]
    010010d9 c3               ret
    

    Notice that the two functions were merged: The addresses are identical. That one fragment of code merely goes by two names. Therefore, when the debugger sees that you've jumped to 0x010010d6, it doesn't know which of the names it should use, so it just picks on.

    That's why it looks like you jumped to the wrong function.

    To disable what is called "identical COMDAT folding", you can pass the /OPT:NOICF flag to the linker.

  • The Old New Thing

    Psychic debugging: Why your expensive four-processor machine is ignoring three of its processors

    • 57 Comments

    On one of our internal mailing lists, someone was wondering why their expensive four-processor computer appeared to be using only one of its processors. From Task Manager's performance tab, the chart showed that the first processor was doing all the work and the other three processors were sitting idle. Using Task Manager to set each process's processor affinity to use all four processors made the computer run much faster, of course. What happened that messed up all the processor affinities?

    At this point, I invoked my psychic powers. Perhaps you can too.

    First hint: My psychic powers successfully predicted that Explorer also had its processor affinity set to use only the first processor.

    Second hint: Processor affinity is inherited by child processes.

    Here was my psychic prediction:

    My psychic powers tell me that

    1. Explorer has had its thread affinity set to 1 proc....
    2. because you previewed an MPG file...
    3. whose decoder calls SetProcessAffinityMask in its DLL_PROCESS_ATTACH...
    4. because the author of the decoder couldn't fix his multiproc bugs...
    5. and therefore set the process thread affinity to 1 to "fix" the bugs.

    Although my first psychic prediction was correct, the others were wide of the mark, though they were on the right track and successfully guided further investigation to uncover the culprit.

    The real problem was that there was a third party shell extension whose authors presumably weren't able to fix their multi-processor bugs, so they decided to mask them by calling the SetProcessAffinityMask function to lock the current process (Explorer) to a single processor. Woo-hoo, we fixed all our multi-processor bugs at one fell swoop! Let's all go out and celebrate!

    Since processor affinity is inherited, this caused every program launched by Explorer to use only one of the four available processors.

    (Yes, the vendor of the offending shell extension has been contacted, and they claim that the problem has been fixed in more recent versions of the software.)

  • The Old New Thing

    Confusion over whether you have Windows XP SP1 or SP2

    • 38 Comments

    Some support people have asked me why the "About" dialog seems to be kind of schizophrenic as to whether a machine has Windows XP SP1 or SP2.

    About Windows

    Microsoft® Windows
    Version 5.1 (Build 2600.xpsp2.040919-1003 : Service Pack 1)
    Copyright© 1981-2001 Microsoft Corporation

    Why does the version string say "xpsp2" and then "Service Pack 1"? Is this machine running SP1 or SP2?

    It's running Service Pack 1. The build number string is a red herring.

    Why does the build number string say "xpsp2" when the computer is running SP1?

    Because Windows XP Service Pack 2 was a victim of changing circumstances.

    After Service Pack 1 shipped, there was no indication that Service Pack 2 was going to be anything other than "just another service pack": A cumulative update of the fixes that had been issued since the release of Service Pack 1. Therefore, the release team created a new project, called it "xpsp2" and when a fix needed to be made to Service Pack 1, they made it there. It was called "xpsp2" because the assumption was that when the time came to release Service Pack 2, they would just take all the fixes they had been making to Service Pack 1 and call that Service Pack 2. In other words, "fixes to Service Pack 1" and "working on Service Pack 2" were the same thing.

    Of course, things changed, and a "new" Service Pack 2 project was created for the "real" Service Pack 2 changes, leaving the old "xpsp2" project to be merely the place where Service Pack 1 fixes were developed.

    Yes, it's confusing. We're kind of embarrassed by the whole project naming fiasco. That's what happens when plans take a radical change after work has already started.

    Anyway, there you have it, the long and boring story of why fixes for Service Pack 1 have "xpsp2" in their build string.

Page 362 of 431 (4,310 items) «360361362363364»