April, 2010

  • The Old New Thing

    How do I switch a window between normal and fullscreen?

    • 34 Comments

    Frederic Delhoume wants to know if there is a simple example of code that switches an application from windowed to fullscreen. He then included a code fragment that did some crazy things with parent windows and hiding and showing.

    You're making it way, way harder than it needs to be. Let's start with our scratch program and make these changes:

    WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
    
    void OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
    {
      DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
      if (dwStyle & WS_OVERLAPPEDWINDOW) {
        MONITORINFO mi = { sizeof(mi) };
        if (GetWindowPlacement(hwnd, &g_wpPrev) &&
            GetMonitorInfo(MonitorFromWindow(hwnd,
                           MONITOR_DEFAULTTOPRIMARY), &mi)) {
          SetWindowLong(hwnd, GWL_STYLE,
                        dwStyle & ~WS_OVERLAPPEDWINDOW);
          SetWindowPos(hwnd, HWND_TOP,
                       mi.rcMonitor.left, mi.rcMonitor.top,
                       mi.rcMonitor.right - mi.rcMonitor.left,
                       mi.rcMonitor.bottom - mi.rcMonitor.top,
                       SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
        }
      } else {
        SetWindowLong(hwnd, GWL_STYLE,
                      dwStyle | WS_OVERLAPPEDWINDOW);
        SetWindowPlacement(hwnd, &g_wpPrev);
        SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
                     SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
      }
    }
    
    // add to WndProc
        HANDLE_MSG(hwnd, WM_LBUTTONUP, OnLButtonUp);
    

    To avoid getting into the intricacies of hotkeys and accelerators, I opted to toggle to fullscreen on a click. When the button goes up, we check whether we are currently in normal mode or fullscreen mode by sniffing at our window styles. If we are in normal mode, we save the current window placement and get the dimensions of the current monitor. And then the magic happens: We remove the caption and other decorations from our window style and reposition the window so it covers the entire monitor. An important flag to pass here is SWP_FRAME­CHANGED, which tells the window manager to recalculate the window decorations (which we need it to do because we just changed them).

    When returning from fullscreen, we just undo what we had done when we went fullscreen: We restore the caption and other decorations to our window and restore the original window state.

    And that's all there is to it. You don't have to do anything special to get the taskbar to "get out of the way"; the taskbar recognizes when applications have gone fullscreen and automatically gets out of the way.

  • The Old New Thing

    He bought the whole seat, but we only needed the edge

    • 28 Comments

    After the Windows 95 project was released to manufacturing, but before the launch event itself, the team finally had a chance to relax and unwind after many years of hard work. The project manager decided to have a morale event to get everyone together to do something fun. A typical morale event might be going to see a baseball game, renting out a movie theater to watch the latest action flick, or something as simple as a picnic or a softball game.

    But this time, the project manager decided to do something different, something wild, something crazy, something everybody would talk about for days: He bought everyone tickets to the monster truck rally. (And he bought the whole seat, even though we'd only need the edge.)

  • The Old New Thing

    How to edit the security attributes of more than one file at a time

    • 86 Comments

    In Windows XP, you could select multiple files, right-click them, then select Properties. The resulting property sheet includes a Security page which lets you edit the security attributes of those files. But when you repeat this exercise on Windows Vista or Windows 7, the Security page is missing. Why doesn't Explorer let you edit the security attributes of more than one file at a time?

    Windows might need to display an elevation prompt if any of the files in the collection require administrator privileges in order to modify the security attributes. The security prompt needs to tell you why you are elevating, but if you selected twenty files, there isn't room to display all twenty of them in the elevation dialog. Truncating the results means that users may be tricked into changing the security of files they didn't intend. "Grant everyone full access to X, Y, Z, and 17 other files?" How do you know your multiselect didn't accidentally include MergerPlans.doc? (Maybe there's some malware that waits for people to change security on multiple items and quietly sneaks NTOSKRNL.EXE into the file list.) Alexander Grigoriev says, "Holding forever to dangerous features is BAD BAD BAD."

    If you need to modify the security attributes on a whole bunch of files, you can use the CACLS program, one of the command line tools that messes with security descriptors. If you want to modify the attributes of all the files in a directory tree, you can edit the security attributes of the root of the tree and indicate that you want to propagate inheritable attributes.

    Pre-emptive hate: "I hate Microsoft for removing this feature." (Okay, that was too tame. A PROPER HATE REQUIRES SENTENCES IN ALL-CAPS.) And you wonder why I don't do Tips/Support topics often. Whenever I provide a tip that lets you work around something, everybody rants about the problem the workaround exists to address.

  • The Old New Thing

    EnumClaw, the function that never was

    • 24 Comments

    bhiggins asks about the mysterious function EnumClaw that existed in some versions of the Win32 documentation.

    I went digging through the MSDN archives and was close to giving up and declaring the cause lost, but then I found it: A copy of the EnumClaw documentation.

    EnumClaw

    The EnumClaw function returns the child or the parent of the window whose HWND is passed in.

    HWND EnumClaw(
      HWND hwndParent    // handle to parent window
    );
    
    Parameters
    hwndParent
    [in] Handle to the parent window.
    Return Values

    If the function succeeds, the return value is the HWND of the child of the hwndParent window. If the window has no child, the return value is the HWND of the parent of the hwndParent window.

    If the function fails, the return value is NULL. To get extended error information, call GetLastError.

    Requirements

      Windows NT/2000/XP: Included in Windows XP and Windows .NET Server.
      Windows 95/98/Me: Unsupported.
      Header: Declared in Winuser.h; include Windows.h.
      Library: Use User32.lib.

    See Also

    Windows Overview, Window Functions.

    There was never a function called EnumClaw. This was a joke inserted by the documentation folks, a pun on the Washington city named Enumclaw. (The state of Washington has a lot of place names which come from Native American words. Other examples are Sequim, Puyallup, and Tulalip. At least Enumclaw is pronounced almost like it's spelled.)

  • The Old New Thing

    A short puzzle about heap expansion

    • 18 Comments

    At the 2008 PDC, somebody stopped by the Ask the Experts table with a question about the heap manager.

    I don't understand why the heap manager is allocating a new segment. I allocated a bunch of small blocks, then freed nearly all of them. And then when my program makes a large allocation, it allocates a new segment instead of reusing the memory I had just freed.

    Under the classical model of the heap, the heap manager allocates a large chunk of memory from lower-level operating system services, and then when requests for memory come in from the application, it carves blocks of memory from the big chunk and gives them to the application. (These blocks are called busy.) When those blocks of memory are freed, they are returned to the pool of available memory, and if there are two blocks of free memory adjacent to each other, they are combined (coalesced) to form a single larger block. That way, the block can be used to satisfy a larger allocation in the future.

    Under the classical model, allocating memory and then freeing it is a net no-operation. (Nitpicky details notwithstanding.) The allocation carves the memory out of the big slab of memory, and the free returns it to the slab. Therefore, the situation described above is a bit puzzling. After the memory is freed back to the heap, the little blocks should coalesce back into a block big enough to hold a larger allocation.

    I sat and wondered for a moment, trying to think of cases where coalescing might fail, like if they happened to leave an allocated block right in the middle of the chunk. Or maybe there's some non-classical behavior going on. For example, maybe the look-aside list was keeping those blocks live.

    As I considered the options, the person expressed disbelief in a different but telling way:

    You'd think the low-fragmentation heap (LFH) would specifically avoid this problem.

    Oh wait, you're using the low-fragmentation heap! This is a decidedly non-classical heap implementation: Instead of coalescing free blocks, it keeps the free blocks distinct. The idea of the low-fragmentation heap is to reduce the likelihood of various classes of heap fragmentation problems:

    • You want to make a large allocation, and you almost found it, except that there's a small allocation in the middle of your large block that is in your way.
    • You have a lot of free memory, but it's all in the form of teeny tiny useless blocks.

    That first case is similar to what I had been considering: where you allocated a lot of memory, free most of it, but leave little islands behind.

    The second case occurs when you have a free block of size N, and somebody allocates a block of size M < N. The heap manager breaks the large block into two smaller blocks: a busy block of size M and a free block of size (N − M). These "leftover" free blocks aren't a problem if your program later requests a block of size N − M: The leftover block can be used to satisfy the allocation, and no memory goes wasted. But if your program never asks for a block of size N − M, then the block just hangs around as one of those useless blocks.

    Imagine, for concreteness, a program that allocates memory in a loop like this:

    • p1 = alloc(128)
    • p2 = alloc(128)
    • free(p1)
    • p3 = alloc(96)
    • (Keep p2 and p3 allocated.)
    • Repeat

    Under the classical model, when the request for 96 bytes comes in, the memory manager sees that 128-byte block (formerly known as p1) and splits it into two parts, a 96-byte block and a 32-byte block. The 96-byte block becomes block p3, and the 32-byte block sits around waiting for somebody to ask for 32 bytes (which never happens).

    Each time through this loop, the heap grows by 256 bytes. Of those 256 bytes, 224 are performing useful work in the application, and 32 bytes are sitting around being one of those useless tiny memory allocations which contributes to fragmentation.

    The low-fragmentation heap tries to avoid this problem by keeping similar-sized allocations together. A heap block only gets re-used for the same size allocation it was originally created for. (This description is simplified for the purpose of the discussion.) (I can't believe I had to write that.)

    In the above scenario, the low-fragmentation heap would respond to the request to allocate 96 bytes not by taking the recently-freed 128-byte block and splitting it up, but rather by making a brand new 96-byte allocation. This seems wasteful. After all, you now allocated 128 + 128 + 96 = 352 bytes even though the application requested only 128 + 96 = 224 bytes. (The classical heap would have re-used the first 96 bytes of the second 128-byte block, for a total allocation of 128 + 128 = 256 bytes.)

    This seemingly wasteful use of memory is really an investment in the future. (I need to remember to use that excuse more. "No, I'm not being wasteful. I'm just investing in the future.")

    The investment pays off at the next loop iteration: When the request for 128 bytes comes in, the heap manager can return the 128-byte block that was freed by the previous iteration. Now there is no waste in the heap at all!

    Suppose the above loop runs 1000 times. A classical heap would end up with a thousand 128-byte allocations, a thousand 96-byte allocations, and a thousand 32-byte free blocks on the heap. That's 31KB of memory in the heap lost to fragmentation, or about 12%. On the other hand, the low-fragmentation heap would end up with a thousand 128-byte allocations, a thousand 96-byte allocations, and one 128-byte free block. Only 128 bytes has been lost to fragmentation, or just 0.06%.

    Of course, I exaggerated this scenario in order to make the low-fragmentation heap look particularly good. The low-fragmentation heap operates well when heap allocation sizes tend to repeat, because the repeated-size allocation will re-use a freed allocation of the same size. It operates poorly when you allocate blocks of a certain size, free them, then never ask for blocks of that size again (since those blocks just sit around waiting for their chance to shine, which never comes). Fortunately, most applications don't fall into this latter category: Allocations tend to be for a set of fixed sizes (fixed-size objects), and even allocations for variable-sized objects tend to cluster around a few popular sizes.

    Generally speaking, the low-fragmentation heap works pretty well for most classes of applications, and you should consider using it. (In fact, I'm told that the C runtime libraries have converted the default C runtime heap to be a low-fragmentation heap starting in Visual Studio 2010.)

    On the other hand, it's also good to know a little of how the low-fragmentation heap operates, so that you won't be caught out by its non-classical behavior. For example, you should now be able to answer the question which was posed at Ask the Experts. As you can see, it often doesn't take much to be an expert. You can do it, too.

    Sidebar: Actually, I was able to answer the customer's question even without knowing anything about the low-fragmentation heap prior to the customer mentioning it. (Indeed, I had barely even heard of it until that point.) Just given the name low-fragmentation heap, I was able to figure out roughly how such a beast would have operated. I wasn't correct on the details, but the underlying principles were good. So you see, you don't even have to know what you're talking about to be an expert. You just have to be able to take a scenario and think, "How would somebody have designed a system to solve this problem?"

  • The Old New Thing

    Email tip: When you say that something didn't work, you have to say how it didn't work

    • 47 Comments

    I illustrate this point with an imaginary conversation, inspired by actual ones I've seen (and, occasionally, been a frustrated party to).

    From: X

    I want to do ABC, but I don't have a DEF. Anybody know of a workaround?

    Somebody has an idea:

    From: Y

    Try mounting this ISO file into a virtual machine and trying the ABC from there.

    Unfortunately, it didn't work:

    From: X

    I tried that, but it didn't work. Any other ideas?

    When somebody suggests a troubleshooting step or a workaround, but when you try it and it doesn't work, you need to say how it didn't work. The person who made the suggestion had some expectation that it would work, and just saying that it didn't work will probably just generate an unhelpful response like "Well, try again." Which doesn't help anybody.

    In this example (which I just made up), a better response from X would be something like this:

    • "I tried that, but it didn't work. Virtual PC refused to load the ISO image, putting up the error message 'The CD image could not be captured. You may not have the proper access privileges to the CD image files.'"
    • "I tried that, but it didn't work. Virtual PC loaded the ISO image, but when I tried to view the contents of the CD, I got 'Not ready reading drive D.'"
    • "I tried that, but it didn't work. Virtual PC loaded the ISO image, but when I double-clicked the ABC file, I got the same error that I got when I tried to do ABC directly."

    Each of these is a different failure mode that suggests a different course of action.

    And then the response probably won't be, "Well, try again."

  • The Old New Thing

    When people ask for security holes as features: Non-administrators reading other users' stuff

    • 14 Comments

    Via the suggestion box, Aaron Lerch asks whether a non-administrator can retrieve/evaluate environment variables as they would appear for another user.

    This falls into the category of asking for a security hole as a feature, specifically an information disclosure security hole, because you are extracting information from a user's private data which has security access controls that do not grant everybody access. Generally speaking, users have full access to their data, as does the operating system itself, but nobody else. Administrators can get access to the data by taking ownership and modifying the ACL or using security overrides like Se­Debug­Privilege, but that's the general idea. And certainly, unprivileged users don't have access to the data from other unprivileged users.

    The way to get a user's initial environment variables is to call the Create­Environment­Block function, passing the token of the user you are interested in. Note that it's more than just scraping the registry, because you also have to take into account group policy objects and the possibility that the information in the registry is incorrect because it is a stale cached roaming profile.

  • The Old New Thing

    Why do non-folders in my shell namespace extension show up in the folder tree view?

    • 12 Comments

    A customer was having trouble with their shell namespace extension:

    When we click the [+] button next to our shell namespace extension in the folder tree view, the tree view shows both files and folders, even though it's supposed to show only folders. Our IShell­Folder::Get­Attributes­Of does return the correct values for SFGAO_FOLDER (including it for the folders and omitting it for the non-folders). What are we doing wrong?

    The tree view enumerates the children of a folder by calling IShell­Folder::Enum­Objects and passing the SHCONTF_FOLDERS flag while omitting the SHCONTF_NONFOLDERS flag. This means that it is only interested in enumerating child folders. Child non-folders should be excluded from the enumeration.

    It so happens that the customer's shell namespace extension was not respecting the SHCONTF_FOLDERS and SHCONTF_NONFOLDERS flags; it always enumerated all objects regardless of what the caller requested.

    Fixing the enumerator fixed the problem.

  • The Old New Thing

    It's a miracle humanity has survived this far, if reaction to the inability to make or receive a telephone call is to be believed

    • 43 Comments

    In one of the mailing lists devoted to chatting among people who work in a particular cluster of Microsoft office buildings, there was some discussion of the quality of mobile phone coverage in the parking garage.

    "I can't get a signal in any of the underground levels. This is intolerable!"

    Here's an idea: Walk to ground level and make your call there.

    "But what if it's an emergency?"

    Then run.

    (Or use one of the emergency phones.)

    Sometimes I wonder how humanity had managed to survive prior to the installation of mobile phone cell towers. Had these people been born just 30 years earlier, they wouldn't have been able to get through everyday life, having never developed their ability to plan anything in advance.

    I remember the days when it was common for people not to be reachable for (gasp) hours at a time. You couldn't even leave a message at the beep; you just had to try again later. It apparently is a miracle that our species didn't go extinct.

  • The Old New Thing

    Why does the wireless connection dialog ask for your password twice?

    • 33 Comments

    Martin wonders why the wireless networking dialog asks you to type your password twice when connecting to an existing network.

    Yeah, that bothers me too, and I don't know why either.

    But while we're on the topic of wireless networking, I thought I'd share a little program that is just as useless as my answer above. (If other people get to hijack the topic, then I want to also.)

    Back in the early days of Windows XP, I found that my wireless networking adapter would constantly disconnect and reconnect. I never figured out why, but I did have a theory. (Theory: The wireless zero configuration service saw another access point and said, "Hey, that access point over there looks much nicer than then one I'm currently connected to. I'm going to drop my current connection and see if maybe that other access point will go out with me." And then it went up to that other access point and asked it out on a date. When the other access point said no, it came crawling back to the original access point. Repeat.)

    Anyway, to avoid this problem (which went away after a while for reasons unclear; maybe it was fixed, maybe whatever situation triggered the problem went away, I didn't bother investigating), I wrote a program which did two very simple things:

    1. If the wireless networking adapter was connected to an access point, then turn off the wireless zero configuration service.
    2. If the wireless networking adapter was not connected to an access point, then turn on the wireless zero configuration service.

    In other words, it automates the process described on this Web page. (I like how that article was copied in its entirety to another site, which replaced the author's name. Now that's chutzpah.)

    Mind you, the program really is no longer interesting in and of itself any more because the underlying problem went away, but I thought it could serve as an illustration of how you can put together some simple things to make a useful tool.

    First, I changed the security descriptor on the wireless zero configuration service so that my account had permission to turn it on and off.

    Second, I added this code to a program that hangs out my Startup group which monitors various things I like to monitor. (I have one program that monitors several things just to cut down on the number of processes hanging around on my machine.) The code has been compressed and reformatted to get rid of the uninteresting parts.

    class MonitorWireless
    {
    public:
      MonitorWireless()
        : m_hWait(NULL)
      {
          ZeroMemory(&m_o, sizeof(m_o));
      }
    
      ~MonitorWireless()
      {
        if (m_hWait) UnregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE);
        if (m_o.hEvent) CloseHandle(m_o.hEvent);
      }
    
      BOOL Initialize();
    
    protected:
      static void CALLBACK s_OnChange(PVOID lpParameter, BOOLEAN)
      {
        MonitorWireless *self =
                   reinterpret_cast<MonitorWireless*>(lpParameter);
        self->CheckIPAddress(); // something changed - check it again
      }
    
      void CheckIPAddress();
      static void StartStopService(BOOL fStart);
    
    private:
        HANDLE m_hWait;
        OVERLAPPED m_o;
    }
    

    The class definition is all very boring. Our class has an OVERLAPPED structure which we use to register for IP address change notifications, and it has a handle to a registered wait, which takes advantage of the thread pool to reduce the number of threads used by the process.

    BOOL MonitorWireless::Initialize()
    {
      m_o.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      if (!m_o.hEvent) return FALSE;
      if (!RegisterWaitForSingleObject(&m_hWait, m_o.hEvent,
                          s_OnChange, this, INFINITE, 0)) return FALSE;
      CheckIPAddress();
      return TRUE;
    }
    

    When the object is initialized, it creates the handle that we will ask to be set whenever the computer's IP address changes, and then registers a wait on that handle with a callback function. When the event is signaled, we check the IP address. And to start the ball rolling, we check the IP address at initialization.

    void MonitorWireless::CheckIPAddress()
    {
      ULONG ulSize = 0;
      if (GetIpAddrTable(NULL, &ulSize, 0) ==
                                         ERROR_INSUFFICIENT_BUFFER) {
        PMIB_IPADDRTABLE piat = reinterpret_cast<PMIB_IPADDRTABLE>
                                    (LocalAlloc(LMEM_FIXED, ulSize));
        if (piat) {
          if (GetIpAddrTable(piat, &ulSize, 0) == ERROR_SUCCESS) {
            BOOL fFound = FALSE;
            for (DWORD dwIndex = 0; dwIndex < piat->dwNumEntries;
                 dwIndex++) {
              PMIB_IPADDRROW prow = &piat->table[dwIndex];
              if (prow->dwAddr == 0) continue;
              if ((prow->wType & (MIB_IPADDR_DYNAMIC |
                                  MIB_IPADDR_DELETED |
                                  MIB_IPADDR_DISCONNECTED)) !=
                                  MIB_IPADDR_DYNAMIC) continue;
              fFound = TRUE;
              break;
            }
            StartStopService(!fFound);
          }
          LocalFree(piat);
        }
      }
    
      HANDLE h;
      NotifyAddrChange(&h, &m_o);
    }
    

    We start by getting the IP address table (doing the standard two-step of first asking how much memory we need to hold it, allocating the memory, and then filling the buffer) and walking through each IP address. If we find an entry with an IP address that is dynamic, not deleted, and not disconnected, then we declare ourselves happy; otherwise we are sad. If we are happy, then we stop the wireless zero configuration service; if we are sad, then we start it.

    void MonitorWireless::StartStopService(BOOL fStart)
    {
      SC_HANDLE sc;
      sc = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT |
                                     SC_MANAGER_ENUMERATE_SERVICE);
      if (sc) {
        SC_HANDLE scWzcsvc = OpenService(sc, TEXT("wzcsvc"),
                       fStart ? SERVICE_START 
                              : SERVICE_STOP | SERVICE_QUERY_STATUS);
        if (scWzcsvc) {
          if (fStart) StartService(scWzcsvc, 0, NULL);
          else        StopService(scWzcsvc);
          CloseServiceHandle(scWzcsvc);
        }
        CloseServiceHandle(sc);
      }
    }
    

    To start or stop the service, we first connect to the service control manager, open the service we want to start/stop, and then, well, start or stop it.

    There is already a Start­Service function, but no Stop­Service function, so I wrote my own:

    void StopService(SC_HANDLE sc)
    {
     SERVICE_STATUS ss;
    
     if (QueryServiceStatus(sc, &ss) &&
         ss.dwCurrentState != SERVICE_STOPPED &&
         ss.dwCurrentState != SERVICE_STOP_PENDING)
       ControlService(sc, SERVICE_CONTROL_STOP, &ss);
    }
    

    If the service is not already stopped (or stopping), then we tell it to stop.

    And there you have it, a program that you don't need any more. But the point here was more to show how you can put together some basic elements to solve a simple problem.

    Techniques illustrated:

    • Registering a wait in the thread pool.
    • Registering asynchronously for IP address changes.
    • Starting and stopping a service.
Page 1 of 3 (24 items) 123