April, 2010

  • The Old New Thing

    The difference between your job and your hobby

    • 34 Comments

    There was an internal discussion about what Microsoft employees should be doing that do not directly relate to their job responsibilities, such as what text editor programmers should be using to write and edit code. Should anybody who uses a programming editor other than Visual Studio be branded a traitor? How about somebody who prefers a smartphone made by a certain Cupertino company? (And for some reason, this discussion took place on the Microsoft bloggers mailing list, because many people consider it a mailing list whose members are bloggers, as opposed to a mailing list for discussing blogging. I happen to adhere to the second definition.)

    These sorts of discussions generate far more heat than light, and I felt compelled to chime in:

    If it doesn't result in a bigger number in my bank account, then it's a hobby. It's noble that many people have Microsoft-focused hobbies. My hobbies are knitting and modern Germanic languages.
  • 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.
  • The Old New Thing

    The mysterious stock bitmap: There's no way to summon it, but it shows up in various places

    • 25 Comments

    A number of stock GDI objects are made available by the Get­Stock­Object function, but one stock GDI object that is mysteriously missing is the stock bitmap. You can't summon the stock bitmap, but it manages to show up in various places, some of them perhaps unexpected.

    The stock bitmap is a monochrome 1×1 bitmap which GDI uses in various places where it has to produce a HBITMAP even though there really isn't any bitmap worth speaking of. In other words, it's used when GDI has to return something but would rather return nothing.

    • When you create a memory DC, the current bitmap selected into it is the stock bitmap.
    • When you create a metafile, the current bitmap is the stock bitmap.

    Every DC and metafile has a current bitmap (which you can retrieve with Get­Current­Object), but when GDI creates a brand new DC or metafile, it doesn't know what bitmap the program is going to pass to Select­Object—after all, predicting the future has yet to be perfected. As a placeholder, it sticks in the dummy static bitmap.

    There has to be a bitmap (as opposed to just leaving it NULL), because the Select­Object function returns the previous object or NULL on failure, so there needs to be a way to tell the difference between "I wasn't able to select the bitmap you requested" and "I was able to select the bitmap you requested, but there was no old bitmap." Returning NULL would also break the common coding pattern:

    // select the new bitmap and save the old one
    HBITMAP hbmPrev = SelectObject(hdc, hbmNew);
        ... do something with hdc ...
    // all done - restore the original bitmap
    SelectObject(hdc, hbmPrev);
    

    If Select­Object had returned NULL when there was no bitmap previously selected into the DC, then the attempt to restore the original bitmap would fail. (Because GDI can't tell whether you passed it a (HBITMAP)NULL or a (HBRUSH)NULL or a (HPEN)NULL or...)

    Normally, a single bitmap cannot be selected into more than one DC, but the stock bitmap has the magical power that it can be selected into multiple DCs at once. Without this magical power, GDI would have to create a different dummy bitmap to select into each newly-created DC and carry it around so that it can be selected back into the DC just before it is destroyed. Seems awful wasteful to allocate an extra bitmap per DC just for this, especially back in the days of 16-bit Windows when GDI heap space was extremely limited.

    There is one more place (that comes to mind) where the stock bitmap appears, and it's somewhat unexpected:

    • When you try to create a 0×y or a x×0 bitmap with the Create­Bitmap or Create­Compatible­Bitmap function you get the stock bitmap back.

    In other words, if you ask for a nothing-bitmap, you get the dummy bitmap back. This is analogous to the case of calling malloc(0), where the implementation is permitted to return a pointer to zero bytes. In other words, malloc(0) can return a non-NULL value which you can't dereference; the only things you can do with it is free() it or realloc() it to something bigger. In the same way that allowing zero-byte allocations simplifies boundary cases of certain algorithms, allowing impossibly thin bitmaps (and returning a dummy handle) may simplify certain graphical algorithms.

    Note however that this behavior of returning the stock bitmap handle when asked to create an impossibly thin bitmap does not apply to the Create­DIB­Section function! If you ask Create­DIB­Section for an impossibly thin bitmap, it returns NULL. So much for consistency.

  • The Old New Thing

    Why are there two values for NoDriveTypeAutoRun which disable Autoplay on drives of unknown type?

    The Windows 2000 Resource Kit described the No­Drive­Type­Auto­Run policy as consisting of a bitfield whose values are as follows:

    Value Meaning
    0x1 Disables Autoplay on drives of unknown type.
    0x4 Disables Autoplay on removable drives.
    0x8 Disables Autoplay on fixed drives.
    0x10 Disables Autoplay on network drives.
    0x20 Disables Autoplay on CD-ROM drives.
    0x40 Disables Autoplay on RAM drives.
    0x80 Disables Autoplay on drives of unknown type.
    0xFF Disables Autoplay on all types of drives.

    Hey, wait, two of the entries are the same. What's the difference between 0x1 (Disables Autoplay on drives of unknown type) and 0x80 (Disables Autoplay on drives of unknown type)?

    The values in the bitfield correspond to return values of the Get­Drive­Type function:

    #define DRIVE_UNKNOWN     0
    #define DRIVE_NO_ROOT_DIR 1
    #define DRIVE_REMOVABLE   2
    #define DRIVE_FIXED       3
    #define DRIVE_REMOTE      4
    #define DRIVE_CDROM       5
    #define DRIVE_RAMDISK     6
    

    The value 0x1 corresponds to bit zero, which means that the Get­Drive­Type function could not tell what type of drive it is.

    On the other hand, the value 0x80 does not correspond to any known return value of Get­Drive­Type. It's reserved for future use.

    My guess as to how this happened is that the original table did not have an entry for 0x80. Then somebody asked, "What does 0x80 mean?"

    The response was, "It's not used yet. It's for some future unknown drive type that has yet to be invented."

    "Okay, thanks. I'll just say that the drive type is unknown."

    Bingo, now you have two copies of "drives of unknown type." The 0x1 means "drives whose type cannot be determined" whereas the 0x80 means "drives of a type not currently known to mankind."

    So let's just pretend that the entry for 0x80 reads "Reserved for future use." (The page is archived content, so it's not like anybody's going to be updating it.)

    Pre-emptive snarky comment: "Autoplay is the stupidest feature on the face of the planet." The issue here is not discussing whether Autoplay is a good idea or bad idea. I'm trying to improve the documentation. I try to avoid providing tips/support about controversial features because of the flame-fest that invariably results. This is an experiment to see if it's safe to go back into the water.

    Update: Comments have been disabled because everybody wants to discuss whether Autoplay is a good idea or not, even though that topic was explicitly taken off the table. Strike two.

  • The Old New Thing

    When you create an object with constraints, you have to make sure everybody who uses the object understands those constraints

    • 19 Comments

    Here's a question that came from a customer. This particular example involves managed code, but don't let that distract you from the point of the exercise.

    I am trying to create a FileStream object using the constructor that takes an IntPtr as input. In my .cs file, I create the native file handle using CreateFile, as shown below.

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern IntPtr CreateFile(string lpFileName,
        int dwDesiredAccess, FileShare dwShareMode,
        IntPtr securityAttrs, FileMode dwCreationDisposition,
        UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile);
    
    IntPtr ptr1 = Win32Native.CreateFile(FileName, 0x40000000, 
             System.IO.FileShare.Read | System.IO.FileShare.Write, 
             Win32Native.NULL, 
             System.IO.FileMode.Create, 
             0xa0000000, // FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
             Win32Native.NULL); 
    

    Then I create the File­Stream object as so:

    FileStream fs = new FileStream(ptr1, FileAccess.Write, true, 1, false);
    

    The fs gets created fine. But when I try to do:

    fs.Write(msg, 0, msg.Length);
    fs.Flush();
    

    it fails with the error "IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations."

    int hr = System.Runtime.InteropServices.Marshal.GetHRForException(e)
    

    Gives hr as COR_E_IO (0x80131620).

    The stack trace is as below.

    System.IO.IOException: IO operation will not work. Most likely
        the file will become too long or the handle was not opened
        to support synchronous IO operations.
    at System.IO.FileStream.WriteCore(Byte[] buffer, Int32 offset, Int32 count)
    at System.IO.FileStream.FlushWrite()
    at System.IO.FileStream.Flush()
    at PInvoke.Program.Main(String[] args)
    

    Can somebody point out what might be going wrong?

    (For those who would prefer to cover their ears and hum when the topic of managed code arises, change FileStream to fdopen.)

    The comment on the line

             0xa0000000, // FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
    

    was provided by the customer, and that's the key to the problem. It was right there in the comment, but the customer didn't understand the consequences.

    As the documentation for Create­File notes, the FILE_FLAG_NO_BUFFERING flag requires that all I/O operations on the file handle be in multiples of the sector size, and that the I/O buffers also be aligned on addresses which are multiples of the sector size.

    Since you created the file handle with very specific rules for usage, you have to make sure that everybody who uses it actually follows those rules. On the other hand, the File­Stream object doesn't know about these rules. It just figures you gave it a handle that it can issue normal synchronous Read­File and Write­File calls on. It doesn't know that you gave it a handle that requires special treatment. And then the attempt to write to the handle with a plain Write­File fails both because the number of bytes is not a multiple of the sector size and because the I/O buffer is not sector-aligned, and you get the I/O exception.

    The solution to this problem depends on what you are trying to accomplish. Why are you passing the FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH flags? Are you doing this just because you overheard in the hallway that it's faster? Well, yes it may be faster under the right circumstances, but in exchange for the increased performance, you also have to follow a much stricter set of rules. And in the absence of documentation to the contrary, you can't assume that a chunk of code actually adheres to your very special rules.

    Like What if two people did this?, this is an illustration of another principle that many people forget to consider when working with objects they didn't write: When you write your own code, do you do this? It's sort of like the Golden Rule of programming.

    Suppose you have a function which accepts a file handle and whose job is to write some data do that file handle. Do you write your function so that it performs all its I/O in multiples of the sector size from buffers which are aligned in memory in multiples of the sector size, on the off chance that somebody gave you a handle that was opened with the FILE_FLAG_NO_BUFFERING flag? Well, no, you don't. You just call Write­File to write to it, and if you want to write 28 bytes, you write 28 bytes. Even if you perform internal buffering and your buffer size happens to be a multiple of the sector size by accident, you still don't align your I/O buffer to the sector size; and when it's time to flush the final partially-written buffer, you have a not-sector-multiple write at the very end anyway.

    If you don't handle this case in your code, why would you expect others to handle it in their code?

    We've seen this principle before, such as when we looked at whether the Process.Refresh method refreshes an arbitrary application's windows.

  • 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

    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

    Why can't you use WM_CTLCOLORSTATIC to change the color of a SS_BLACKRECT?

    • 1 Comments

    If you specify one of the static control styles which draw a frame or rectangle

    SS_BLACKRECTSS_BLACKFRAME
    SS_GRAYRECTSS_GRAYFRAME
    SS_WHITERECTSS_WHITEFRAME

    the control will be drawn with the corresponding color (which, as we saw last time, isn't actually black, gray, or white). If you try to customize the color by handling the WM_CTL­COLOR­STATIC message, you'll find that it has no effect.

    Well, yeah, because you said you wanted a black rectangle.

    If you want some other color, you could set the style to SS_OWNER­DRAW then draw the solid color in your WM_DRAW­ITEM handler. Or you can just use a text static control with no text. In that case, you can respond to WM_CTL­COLOR­STATIC in the usual way. Since you specified no text, all that will be drawn is the background color.

  • The Old New Thing

    Why doesn't SS_WHITERECT actually draw a white rectangle?

    • 11 Comments

    There are six styles available to the static controls which draw frames and rectangles in one of three fixed colors:

    SS_BLACKRECTSS_BLACKFRAME
    SS_GRAYRECTSS_GRAYFRAME
    SS_WHITERECTSS_WHITEFRAME

    But if you actually create a static control with one of these styles, you'll find that the color isn't actually black, gray, or white.

    So why are they called black, gray, and white?

    Because they used to be black, gray, and white.

    Knowledge Base article 125684 gives the history behind these styles. Back in the 16-bit days, these styles really did give you black, gray, and white, or at least they did if you used the default color scheme. Windows 95 shifted from using the window colors to the 3D colors, presumably to give the system a less harsh look.

  • 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.

Page 2 of 3 (24 items) 123