• The Old New Thing

    Weight Gain 4000, the competition

    • 9 Comments

    Some years ago, one of my colleagues mentioned at the lunch table, "I went hiking this weekend, and man, my backpack was so heavy. I weighed it, and it was like 35 pounds. And then I realized, wait a second, I'm overweight by 35 pounds. I'm carrying this heavy backpack all the time!"

    Thus began a collective weight loss competition we called Weight Gain 4000, named after an episode of South Park which had aired recently. ("I'm not fat; I'm big-boned!") I set up a Web page where people could enter their current weight, and it charted everyone's pounds over target weight as a function of time. Oh, and the goal was to lose weight, not gain it.

    It so happened that I won this little competition with the aid of some extra bike rides. Then again, I also had the least amount of weight to lose. And my colleagues accused me of cheating because I managed to accelerate my weight loss as the deadline approached. (But I didn't cheat, honest!)

    I was reminded of this competition from years ago when I read about John Dirks and Adam Orkand who also learned that an effective way to make you stick to your weight loss plan is to make a wager out of it.

  • The Old New Thing

    What is the hSection parameter to CreateDIBSection for?

    • 9 Comments

    The CreateDibSection function creates a special type of bitmap known as a DIB section. We've worked with these guys before: The feature of DIB sections that is by far the most interesting is that the raw pixels in the bitmap are mapped into your process space as if they were normal memory, which you can read from and write to directly.

    But what is the deal with that funky hSection parameter? Although the ppvBits parameter receives "a pointer to the location of the DIB bit values," the documentation also says that if you pass NULL, then "an application cannot later obtain a handle to this memory." That second part makes no sense. Why would I want to obtain a handle to the memory if I passed NULL, since I told it I didn't have any memory to begin with? Why is it so important to call out that I can't retrieve NULL? The documentation appears to go out of its way to point out something that makes no sense!

    Let's look at that second part in a little more context. Here is the entire description for the hSection parameter:

    If hSection is NULL, the system allocates memory for the DIB. In this case, the CreateDIBSection function ignores the dwOffset parameter. An application cannot later obtain a handle to this memory. The dshSection member of the DIBSECTION structure filled in by calling the GetObject function will be NULL.

    The "this" in "this memory" is "the memory the system allocated," not "the memory the application passed in." Because, as you noticed, the application didn't pass in any memory! It's trying to tell you that when you tell the system to allocate memory for the DIB section, you don't get to peek back in and get the handle to the memory which the system allocated.

    Let's look at the bigger picture here. The memory for a DIB section needs to be mapped into the application's address space, and from the description, the application has the option of passing explicit storage in the form of a file mapping handle (and offset into that file mapping handle), or it can pass NULL and let GDI worry about where to store it. If you were writing the GDI code to manage the memory for DIB sections, how would you do it?

    How would you do it? is is another one of those problem-solving questions similar to What would the world be like if that were true? or Imagine if two programs did this? For one thing, How would you do it? lets you rule out designs that involve clairvoyance or psychic powers. (It's surprising how often people quietly assume that systems are built upon clairvoyance.) For another thing, it forces you to put on a different hat and view the problem from another point of view, one which may help you understand the system better.

    If you had to implement a system where memory could be managed either in the form of a file mapping handle or a mechanism of your choice, you probably would choose as your alternate mechanism... a file mapping handle. That way, there is only one code path for memory management instead of two.

    Suppose you had to install a door security system that both you and Bob could use. Bob insists that he use a traditional metal key to unlock the door, but says you can use any system you want. Would you design a combination system that could be unlocked either with a metal key or an electronic smart card? Or would you just use a traditional keyed lock with two sets of keys, giving one of Bob and keeping the other for yourself?

    When you create a DIB section and pass NULL for the hSection, GDI simply creates an internal hSection and uses that. The documentation is trying to say that the internal hSection is inaccessible to the application.

    Note: I don't know if this is actually what happens internally, but it's the simplest explanation that matches the known facts.

  • The Old New Thing

    Private classes, superclassing, and global subclassing

    • 9 Comments

    In the suggestion box, A. Skrobov asks why it's impossible to superclass WC_DIALOG, but the example that follows is not actually superclassing.

    When I register my own class under this atom, and leave NULL in WNDCLASS.hInstance, Windows fills it in for me. Then I have two distinct classes registered: (0,WC_DIALOG) and (hMyInstance,WC_DIALOG), and DialogBox functions all use the first one.

    This question is a bit confused, since it says that the goal is to superclass the dialog class, but registering WC_DIALOG is not superclassing.

    First, I'll refer everyone to this MSDN article which describes the various ways of manipulating a window class: Subclassing, superclassing, and global subclassing.

    To superclass the dialog class, you retrieve information about the class by calling GetClassInfo and then register a new class based on the original class. But you don't need to go to all that effort to superclass the dialog class, because you already know what you need to know: The number of extra bytes is DLGWINDOWEXTRA, and the dialog procedure is DefDlgProc. You can just register your superclass directly, as we saw last time.

    Superclassing is done by registering your custom class under a different name, and using that class name if you want to obtain the new behavior. On the other hand, the question about talks about registering a class under the same name as the original (namely, WC_DIALOG). This isn't subclassing, nor is it superclassing, nor is it even global subclassing.

    Before continuing the discussion, I'll first address the issue of leaving NULL in WNDCLASS.hInstance: The value NULL for the instance handle is not legal when registering a class. Each class is associated with a module instance, and NULL is not a module instance. The window manager autocorrects this mistake by registering the class under the module corresponding to the executable. This is the same special-case behavior you get if you call GetModuleHandle(NULL), so it's not something completely out of the blue. It looks like A. Skrobov is being confused by the window manager's attempt to do what you mean. So much for being helpful.

    Okay, back to the original problem. Recall that the HINSTANCE member of the WNDCLASS structure is used to specify the class namespace. If you register a class against the handle of the current executable, then in order to create a window with that class, you need to create it with that same instance handle.

    Now we can put all the pieces together: Registering the class with WNDCLASS.hInstance = NULL is autocorrected to registering it with WNDCLASS.hInstance = GetModuleHandle(NULL), which places the class in the window class namespace of the current module. This is a separate class from the system dialog class, which is registered against GetModuleHandle(TEXT("USER32")). The two are registered against different modules, so they live independent lives. They just happen to have the same name.

    As we learned a few years ago, the instance handle you pass to the CreateWindow (or related) function is used to look up the window class, and as we also learned, the HINSTANCE you pass to the DialogBox (or related) function is used to look up the template as well as to create the frame window. The class name comes from the template, and if you didn't specify an explicit class in your template, then the dialog manager will use WC_DIALOG.

    You now have all the pieces necessary to understand what is going on. When you register the class against your executable's instance, you need to use that same instance when creating the dialog box so that your private class is found instead of the global one.

    To show how this all fits together, I've written a little program which registers a private class which happens to have the name WC_DIALOG and then uses it to create a dialog box.

    // scratch.rc
    #include <windows.h>
    
    // A pointless dialog box, for illustration only
    1 DIALOG 0,0,150,50
    STYLE DS_MODALFRAME | DS_SHELLFONT | WS_POPUP | WS_VISIBLE |
        WS_CAPTION | WS_SYSMENU
    CAPTION "Pointless"
    FONT 8, "MS Shell Dlg"
    BEGIN
        DEFPUSHBUTTON "Cancel",IDCANCEL,50,18,50,14
    END
    
    // scratch.cpp
    #include <windows.h>
    
    LRESULT CALLBACK
    SuperDlgProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
    {
      switch (uiMsg) {
      case WM_ERASEBKGND:
        return DefWindowProc(hwnd, uiMsg, wParam, lParam);
      }
      return DefDlgProc(hwnd, uiMsg, wParam, lParam);
    }
    
    INT_PTR CALLBACK
    DlgProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam)
    {
      switch (wm) {
      case WM_INITDIALOG: return TRUE;
      case WM_CLOSE: EndDialog(hwnd, 0); return TRUE;
      }
      return FALSE;
    }
    
    int CALLBACK
    WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
            LPSTR pszCmdLine, int nShowCmd)
    {
      WNDCLASS wc;
      wc.style = 0;
      wc.lpfnWndProc = SuperDlgProc;
      wc.cbClsExtra = 0;
      wc.cbWndExtra = DLGWINDOWEXTRA;
      wc.hInstance = hinst;
      wc.hIcon = NULL;
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = (HBRUSH)(COLOR_INFOBK + 1);
      wc.lpszMenuName = NULL;
      wc.lpszClassName = WC_DIALOG;
    
      if (RegisterClass(&wc))
        DialogBox(hinst, MAKEINTRESOURCE(1), NULL, DlgProc);
    
      return 0;
    }
    

    The dialog template is itself entirely unremarkable; it looks like any old dialog template.

    Our superclass takes the regular dialog box class and gives it a custom background color, namely COLOR_INFOBK.

    The program registers this private version of WC_DIALOG and creates a dialog box based on it. Since we passed the same HINSTANCE in the WNDCLASS.hInstance as we did to DialogBox, the lookup of the WC_DIALOG class will find our private version and use it instead of the global version.

  • The Old New Thing

    Can you create an information context for the display?

    • 9 Comments

    Adrian McCarthy asks, "Can you create an information context for the display? ... I can call CreateDC("DISPLAY"), but perhaps that wouldn't generalize for a multiple-monitor display with different settings on each screen. I'm trying to avoid constantly creating and destroying DCs when all I need to do is measure strings, check color depth, dpi, etc."

    I admire the effort of trying to avoid creating a whole DC when all you want is to perform some inquiries. Some inquiries are monitor-independent, like getting the DPI or measuring strings, so you can just use GetDC(NULL) to get a temporary DC. This is cheaper than a full-on CreateDC since GetDC goes through the DC cache, so you're just grabbing a DC out of the cache temporarily, doing a little computation, and then returning it to the cache (with ReleaseDC).

    If you are doing something that is monitor-specific, like getting its color depth, you can call EnumDisplayMonitors on the desktop DC to look at each monitor.

    (And just for completeness, to get the name for a specific monitor if you really do want to create an IC for it, call GetMonitorInfo with a MONITORINFOEX and look at the szDevice member.)

    Update: Original text said "DC" in the last sentence by mistake.

  • The Old New Thing

    Walt Mosspuppet: The return of the fake blog

    • 9 Comments

    Fake Steve Jobs put on the map the wonderful insanity of the fake celebrity blog. (I'm sure there were others before Fake Steve Jobs, but that's the one that made it cool and hip.) Copycats sprung up, from Fake Steve Ballmer to Mock Mark Cuban, but none of them really had the staying power of good old Fake Steve Jobs.

    (movie trailer voice) Until now.

    Introducing Walt Mosspuppet, a fake video blog starring a puppet version of the technology reporter.

    I love this guy.

  • The Old New Thing

    Follow-up: A new DUI record set in the state of Washington

    • 9 Comments

    A year ago, I noted that a new DUI record had been set for the state of Washington. It took a while, but the story finally settled out.

    Recapping the story so far (links in the original article): The driver's attorneys eventually succeeded in having her released from jail (where she had been held on $300,000 bail) to a treatment center.

    In April 2008, the driver pled guilty to driving while intoxicated and in June 2008 was sentenced to one year in prison for that offense, plus additional days for other offenses, totalling 440 days in jail; plus 90 days of electronic home monitoring, fines, attendance at alcohol-education classes, and other unspecified penalties. That last article also runs down the four separate traffic incidents the driver has been involved in: Driving on the shoulder past a line of stopped cars and becoming involved in a three-car collision, the incident which raised so much public attention. A hit-and-run accident. And two more incidents of driving while intoxicated.

    Unfortunately, permanent suspension of driving privileges was not listed among the penalties.

    Other high blood alcohol levels:

  • The Old New Thing

    xkcd breaks the news on the new Windows 7 user interface

    • 9 Comments

    Last week, Web comic xkcd covered the new Windows 7 user interface. Unfortunately, they got the wrong operating system. It was Windows XP that had a picture of Hitler (according to a few of our beta testers).

  • The Old New Thing

    The Ballard Locks will be empty this week

    • 9 Comments

    The Hiram M. Chittenden Locks (more commonly known as the Ballard Locks) are a common attraction in Seattle. But if you pay a visit for the next week, you'll find that the large set of locks will be empty. And not just empty of boats. Empty of water.

    The chamber has been emptied of water for annual maintenance, and if you stop by, you can take pictures of a big hole in the ground.

    According to The Seattle Times, items found upon draining the chamber include cell phones, a two-way radio, a boot, beer cans, and prescription glasses. Nothing too exciting, I have to admit. Now, if they had found a bicycle...

  • The Old New Thing

    Rob Cockerham investigates those companies that pay cash for gold

    • 9 Comments

    Rob Cockerham seems to have a lot of spare time, which is great for the rest of us, because he investigates all those things we simply don't have the time for, and then posts the results on his Web site ("The sixth-best website in the world").

    Today's highlight is a pair of investigations he performed some time ago which seem to show two sides of one industry.

    That Web site is a sinkhole of time-wastage. If you're not careful, you'll find yourself clicking around from story to story, like the How much is inside? adventures, in which he investigates things like how many threads per inch are there in 360-thread count pillowcases? Or his gallery of costumes (for Hallowe'en or other events), including my favorite: Paparazzi.

  • The Old New Thing

    If you want to consume all the virtual address space, well, then go ahead and consume it, you don't need my help

    • 9 Comments

    Commenter Matthew Chaboud asks if there's an easy way to consume all the virtual address space below 4GB, short of, well, actually allocating it. "It seems like there should be a cleaner way to do this."

    If you want to consume all the virtual address space, then call VirtualAlloc until you turn blue. Programs shouldn't care what address they get back from a memory allocation function; they should handle values below 2GB and above 2GB with equal facility.

    It's not like there's a ConsumeAllAvailableVirtualAddressSpaceAndExhaustTheHeap function. (Is there a AllocateAllRemainingDiskSpaceAndFillExistingFilesWithZeroes function?) What would be the point of such a function? Once you call it, you have run out of memory!

    If Mr. Chaboud is talking about keeping programs away from bottom 4GB of virtual address space on a 64-bit machine, then a much easier way to do this is to set the AllocationPreference configuration setting to specify that memory should be allocated from high addresses first. (But I don't think that's the scenario that prompted the original question, because on 64-bit Windows, the default heap is above the 4GB boundary, so there would be no need to exhaust the heap in order to consume the memory at virtual addresses below 4GB.)

    Correction: Pavel Lebedinsky points out that the default heap is below 4GB on 64-bit machines. It used to be above the 4GB boundary on earlier versions of 64-bit Windows, but I guess they changed it.

Page 374 of 455 (4,546 items) «372373374375376»