• The Old New Thing

    Why are the dimensions of a maximized window larger than the monitor?

    • 23 Comments

    When you inspect the window rectangle of a maximized window, you might notice that its dimension are actually slightly larger than the screen. The upper left corner is something like (−8, −8) and the lower right corner is correspondingly eight pixels beyond the bottom right corner of the screen. Why is that?

    The extra eight pixels that hang off the screen are the window borders. When you maximize a window, the window manager arranges things so that the client area of the window fills the width of your work area, and it plus the caption bar fills the height of the work area. After all, you want to see as much of your document as possible; there's no need to show you the window borders that aren't doing you any good. (It leaves the caption bar on the screen for obvious reasons.)

    These extra pixels hanging off the edge of the screen don't normally cause any distress. At least, not until you have multiple monitors. And then they kind of annoy you, because they bleed over into the adjacent monitor.

    Oh well.

    Bonus chatter: When the taskbar was originally written, multiple monitor support did not yet exist in Windows, and the taskbar took advantage of this by simply letting its borders hang off screen, and when it went into auto-hide mode, it just moved itself off-screen save for a sliver of pixels. When multiple monitor support was added, those pixels bled over into adjacent monitors, and in the case of an auto-hide taskbar, you could see where the taskbar was hiding, because it merely slide onto the adjacent monitor. The taskbar fixed this by explicitly clipping itself to the target monitor.

  • The Old New Thing

    Setting up a new computer often results in leftover slot covers, so what do you do with the slot covers?

    • 30 Comments

    When I joined Microsoft, I had a brand new computer waiting for me. One of the rites of passage for new employees is setting up their computer. A colleague helped me out with this effort, and one of the steps he performed was installing the network card. (Back in the day, network adapters were not integrated into the motherboard. If you wanted one, you had to buy an add-on card.)

    Now, when you install a network card, it occupies a slot, and you need to remove the corresponding slot cover because the card comes with its own integrated slot cover. When my colleague removed the old slot cover from the computer, he didn't just toss it into the garbage. He folded it back on itself with the tab on the top, like this: ᔪ

    What the heck was that all about?

    He didn't give me time to ask the question because he immediately walked over to my office door, pulled it all the way open, and then jammed the doodad under the door. The tab at the top caught the door, and the angled bottom part dug into the carpet, holding the door open:

    In other words, he took the slot cover and converted it into a doorstop.

    Thereafter, I noticed that all of my coworkers used slot covers to hold their office doors open. It was part of the team's tribal knowledge.

    (This trick is lost to history not only because computers nowadays come with all the peripherals you would typically need, but also because the new office doors are not spring-loaded.)

  • The Old New Thing

    Connecting some blocks: Tell me more about the current image in my wallpaper slide show

    • 11 Comments

    One of my colleagues said,

    I really like the wallpaper slide show feature, especially the one that rotates through the top-rated pictures in my photo library. My photo library includes photos taken by other members of my family, and sometimes I'll get a wallpaper that I want to learn more about. It'd be great if there were some way to say "Hey, tell me more about this wallpaper."

    Today's Little Program snaps together some blocks.

    • It registers a command on the desktop background.
    • The command looks at the current wallpaper image.
    • And then shows you some information about it in the form of a property sheet.

    (My colleague actually preferred that the image be opened in Photo Gallery, but I'll just show the properties because not every has Photo Gallery installed, and the purpose of the exercise is to show how to snap blocks together, not to argue about which color blocks to use.)

    #define UNICODE
    #define _UNICODE
    #define STRICT
    #include <windows.h>
    #include <shlobj.h>
    #include <shellapi.h>
    #include <atlbase.h>
    #include <atlalloc.h>
    
    void ShowProperties(PCWSTR pszFile)
    {
     SHELLEXECUTEINFO sei = { sizeof(sei) };
     sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_INVOKEIDLIST;
     sei.nShow = SW_SHOWNORMAL;
     sei.lpVerb = L"properties";
     sei.lpFile = pszFile;
     ShellExecuteEx(&sei);
    }
    
    int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
    {
     CCoInitialize init;
     ProcessReference ref;
    
     // The rest of this code is adapted from
     // Obtaining information about the user's wallpaper on multiple monitors
    
     CComPtr<IDesktopWallpaper> spdw;
     CoCreateInstance(CLSID_DesktopWallpaper, nullptr, CLSCTX_ALL,
                      IID_PPV_ARGS(&spdw));
    
     CComHeapPtr<wchar_t> spszCommonWallpaper;
     HRESULT hr = spdw->GetWallpaper(nullptr, &spszCommonWallpaper);
     if (hr == S_OK) {
      // Single wallpaper on all monitors.
      ShowProperties(spszCommonWallpaper);
     } else if (hr == S_FALSE) {
      // Different wallpaper on each monitor.
      // Look for the monitor that has the mouse pointer.
      POINT pt;
      GetCursorPos(&pt);
    
      UINT count;
      spdw->GetMonitorDevicePathCount(&count);
    
      for (UINT i = 0; i < count; i++) {
       // Get the device path for the monitor.
       CComHeapPtr<wchar_t> spszId;
       spdw->GetMonitorDevicePathAt(i, &spszId);
    
       // Get the monitor location.
       RECT rc;
       spdw->GetMonitorRECT(spszId, &rc);
       if (PtInRect(&rc, pt)) {
        // Get the wallpaper on that monitor.
        CComHeapPtr<wchar_t> spszWallpaper;
        hr = spdw->GetWallpaper(spszId, &spszWallpaper);
        ShowProperties(spszWallpaper);
        break;
       }
      }
     }
    
     return 0;
    }
    

    Now we get to hook this up to the context menu for the desktop.

    [HKEY_CLASSES_ROOT\DesktopBackground\shell\Wallpaper properties\command]
    @="WallpaperProperties.exe"
    

    Now you can right-click on the desktop background and select Wallpaper properties, and it will show you the properties of the wallpaper. You are of course welcome to do something else, like load the wallpaper into your favorite image viewer.

    Note that I cheated a bunch here. In addition to pretty much ignoring all error checking (because this is a Little Program), I also use the current mouse position instead of using the point at which the context menu was invoked.

  • The Old New Thing

    Solving the problem rather than answering the question: How can a non-administrator modify a registry key that requires administrator permission?

    • 32 Comments

    A customer opened with a question, which the customer liaison forwarded to the product group with High Priority. (Because, apparently, their customer is more important than any other customer.)

    Our program needs to modify a registry key in HKEY_LOCAL_MACHINE even when not running as an administrator. We tried setting an entry in the registry key HKLM\Software\Microsoft\Windows NT\Current­Version\App­Compat­Flags\Layers to run our application elevated, but it didn't help. We also tried setting the entry in our application manifest to say that it requires elevation, but that didn't work either. Please provide us with a way to override UAC.

    The fact that they went in and tried to enable an application compatibility setting on their own application means that instead of fixing their program, they are just going to keep throwing garbage on the sidewalk and hope the street sweeper will still come and clean up behind them.

    Upon closer questioning, they explained that setting the manifest entry didn't work in the sense that when the user ran the program, the operating system prompted for elevation. But they wanted their program to elevate without prompting.

    Okay, first of all, if any program could could elevate without prompting, then there would be no point to elevation. Every program would simply ask for secret unprompted elevation, and there would be no point to elevation in the first place. So that angle is a non-starter.

    We asked them for details on the problem they are having, the problem where they think writing to HKLM is the solution. That way, we can solve the problem instead of answering the question.

    When our program is installed, it prompts the person doing the installation for the name of the server to connect to. The installer writes this information to HKLM. When a non-administrator runs the program, we want them to be able to switch to a different server. That's why we need to be able to write to HKLM.

    Okay, now that we understand the scenario, we can provide a solution.

    First of all, the reason why you can't write to HKLM is that it would allow a non-administrative user to change the server settings of another user. Suppose that I run the program and change the server to http://evil-hackers.example.com. Then I log off and wait. The next person to use the computer and run the program will connect to the hacker site instead of the real site, and now I can harvest credentials or launch a man-in-the-middle attack or otherwise do bad things.

    The solution, then, is to reduce the scope of any changes a non-administrative user makes to just that user. That way, if they choose to point the program at a rogue server, they are merely attacking themselves.

    • At install time, write the default server information to HKLM.
    • When a user changes the server, write the new server to HKCU.
    • When the program needs to decide which server to connect to:
      • Check if there is a local setting in HKCU. If so, then use it.
      • If there is no setting in HKCU, then use the setting in HKLM.
    • Optional: Add an administrative option to change the default server in HKLM.

  • The Old New Thing

    RegNotifyChangeKeyValue sucks less

    • 11 Comments

    One of the gotchas of the Reg­Notify­Change­Key­Value function is that the notification registration has thread affinity. This is a problem if you want the notification registration to outlive the thread that generated it. In particular, if you register the notification from a nonpersistent thread pool thread, you get into an infinite loop:

    1. Thread pool task calls Reg­Notify­Change­Key­Value, and waits for the associated event via Register­Wait­For­Single­Object.
    2. Thread pool thread goes idle.
    3. Thread pool destroys the idle thread.
    4. Due to thread affinity, this signals the handle.
    5. The thread pool queues a task to process the handle that was signaled.
    6. The task checks the registry key (observes that nothing changed) and calls Reg­Notify­Change­Key­Value again.
    7. Repeat.

    Windows 8 added a new flag to the Reg­Notify­Change­Key­Value function: REG_NOTIFY_THREAD_AGNOSTIC. If you pass this flag, then the notification registration does not have thread affinity. If the thread that called Reg­Notify­Change­Key­Value exits, the notification registration remains active.

  • The Old New Thing

    Why does the mouse cursor jump a few pixels if you click on the very bottom row of pixels on the taskbar?

    • 31 Comments

    ABCDSchuetze discovered that if you click on the very bottom row of pixels of the taskbar, the mouse cursor jumps up a few pixels. Why is that?

    In order to take advantage of Fitts's Law, the bottom-most row of pixels on a bottom-docked taskbar are clickable. Even though they are technically on the dead border of the window, the taskbar redirects the click to the button immediately above the border. But then you have this problem:

    • User clicks on the border pixel between the button and the edge of the screen.
    • The taskbar remaps the click to the button, thereby activating the button.
    • The button takes capture because that's what buttons do when you click on them. This allows you to drag off the button to cancel the click.
    • But wait: Since the mouse is on the border, it is already outside the button.
    • Result: The button cancels immediately.

    The short version: Clicking on the Fitts's edge causes the button to be pressed and then immediately canceled.

    The fix is to nudge the mouse back inside the button when the click begins.

  • The Old New Thing

    Nice job, you got an A minus from Bill

    • 15 Comments

    Bill Gates does not praise lightly.

    Some time ago, a colleague of mine helped to prepared a keynote address for Bill Gates. Afterward, he was informed that Bill rated the presentation an "A minus".

    My colleague thought, "Wow, an A minus. It would be great to get some feedback from Bill about where I could have done better."

    Bill's assistant explained, "Don't worry. That's the highest grade he gives."

  • The Old New Thing

    Further adventures in trying to guess what encoding a file is in

    • 8 Comments

    The Is­Text­Unicode function tries to guess the encoding of a block of memory purporting to contain text, but it can only say "Looks like Unicode" or "Doesn't look like Unicode", and there some notorious examples of where it guesses wrong.

    A more flexible alternative is IMulti­Language2::Detect­Code­page­In­IStream and its buffer-based equivalent IMulti­Language2::Detect­Input­Code­page. Not only can these methods detect a much larger range of code pages, they also can report multiple code pages, each with a corresponding confidence level.

    Here's a Little Program that takes the function out for a spin. (Remember, Little Programs do little to no error checking.)

    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <shlwapi.h>
    #include <ole2.h>
    #include <mlang.h>
    #include <shlwapi.h>
    #include <atlbase.h>
    #include <stdio.h>
    
    bool IsHtmlFile(PCWSTR pszFile)
    {
     PCWSTR pszExtension = PathFindExtensionW(pszFile);
     return
      CompareStringOrdinal(pszExtension, -1,
                           L".htm", -1, TRUE) == CSTR_EQUAL ||
      CompareStringOrdinal(pszExtension, -1,
                            L".html", -1, TRUE) == CSTR_EQUAL;
    }
    
    int __cdecl wmain(int argc, wchar_t **argv)
    {
     if (argc < 2) return 0;
     CCoInitialize init;
     CComPtr<IStream> spstm;
     SHCreateStreamOnFileEx(argv[1], STGM_READ, 0, FALSE, nullptr, &spstm);
    
     CComPtr<IMultiLanguage2> spml;
     CoCreateInstance(CLSID_CMultiLanguage, NULL,
         CLSCTX_ALL, IID_PPV_ARGS(&spml));
    
     DetectEncodingInfo info[10];
     INT cInfo = ARRAYSIZE(info);
    
     DWORD dwFlag = IsHtmlFile(argv[1]) ? MLDETECTCP_HTML
                                        : MLDETECTCP_NONE;
     HRESULT hr = spml->DetectCodepageInIStream(
         dwFlag, 0, spstm, info, &cInfo);
     if (hr == S_OK) {
      for (int i = 0; i < cInfo; i++) {
       wprintf(L"info[%d].nLangID = %d\n", i, info[i].nLangID);
       wprintf(L"info[%d].nCodePage = %d\n", i, info[i].nCodePage);
       wprintf(L"info[%d].nDocPercent = %d\n", i, info[i].nDocPercent);
       wprintf(L"info[%d].nConfidence = %d\n", i, info[i].nConfidence);
      }
     } else {
      wprintf(L"Cannot determine the encoding (error: 0x%08x)\n", hr);
     }
     return 0;
    }
    

    Run the program with a file name as the command line argument, and the program will report all the detected code pages.

    One thing that may not be obvious is that the program passes the MLDETECTCP_HTML flag if the file extension is .htm or .html. That is a hint to the detector that it shouldn't get faked out by text like <body> and think it found an English word.

    Here's the output of the program when run on its own source code:

    info[0].nLangID = 9
    info[0].nCodePage = 20127
    info[0].nDocPercent = 100
    info[0].nConfidence = 83
    info[1].nLangID = -1
    info[1].nCodePage = 65001
    info[1].nDocPercent = -1
    info[1].nConfidence = -1
    

    This says that its first guess is that the text is in language 9, which is LANG_ENGLISH, code page 20127, which is US-ASCII, That text occupies 100% of the file, and the confidence level is 83.

    The second guess is that the text is in code page 65001, which is UTF-8, but the confidence level for that is low.

    The language-guessing part of the function is not very sophisticated. For a higher-quality algorithm for guessing what language some text is in, use Extended Linguistic Services. I won't bother writing a sample application because MSDN already contains one.

  • The Old New Thing

    Non-capturing C++ lambdas can be converted to a pointer to function, but what about the calling convention?

    • 24 Comments

    First, let's look at how lambdas are implemented in C++.

    It is similar in flavor to the way lambdas are implemented in C#, but the details are all different.

    When the C++ compiler encounters a lambda expression, it generates a new anonymous class. Each captured variable becomes a member of that anonymous class, and the member is initialized from the variable in the outer scope. Finally, the anonymous class is given an operator() implementation whose parameter list is the parameter list of the lambda, whose body is the lambda body, and whose return value is the lambda return value.

    I am simplifying here. You can read the C++ language specification for gory details. The purpose of this discussion is just to give a conceptual model for how lambdas work so we can get to answering the question. The language also provides for syntactic sugar to infer the lambda return type and capture variables implicitly. Let's assume all the sugar has been applied so that everything is explicit.

    Here's a basic example:

    void ContainingClass::SomeMethod()
    {
     int i = 0, j = 1;
     auto f = [this, i, &j](int k) -> int
        { return this->calc(i + j + k); };
     ...
    }
    

    The compiler internally converts this to something like this:

    void ContainingClass::SomeMethod()
    {
     int i = 0, j = 1;
    
     // Autogenerated by the compiler
     class AnonymousClass$0
     {
     public:
      AnonymousClass$0(ContainingClass* this$, int i$, int& j$) :
       this$0(this$), i$0(i$), j$0(j$) { }
      int operator(int k) const
         { return this$0->calc(i$0 + j$0 + k); }
     private:
      ContainingClass* this$0; // this captured by value
      int i$0;                 // i captured by value
      int& j$0;                // j captured by reference
     };
    
     auto f = AnonymousClass$0(this, i, j);
     ...
    }
    

    We are closer to answering the original question. but we're not there yet.

    As a special bonus: If there are no captured variables, then there is an additional conversion operator that can convert the lambda to a pointer to a nonmember function. This is possible only in the case of no captured variables because captured variables would require an AnonymousClass$0 instance parameter, but there is nowhere to pass it.

    Here's a lambda with no captured variables.

    void ContainingClass::SomeMethod()
    {
     auto f = [](int k) -> int { return calc(k + 42); };
     ...
    }
    

    The above code gets transformed to

    void ContainingClass::SomeMethod()
    {
     class AnonymousClass$0
     {
     public:
      AnonymousClass$0()  { }
      operator int (*)(int k) { return static_function; }
      int operator(int k) const { return calc(k + 42); }
     private:
      static int static_function(int k) { return calc(k + 42); }
     };
    
     auto f = AnonymousClass$0();
     ...
    }
    

    Okay, now we can get to the actual question: How can I specify the calling convention for this implicit conversion to a pointer to nonmember function?

    (Note that calling conventions are not part of the C++ standard, so this question is necessarily a platform-specific question.)

    The Visual C++ compiler automatically provides conversions for every calling convention. So with Visual C++, the transformed code actually looks like this:

    void ContainingClass::SomeMethod()
    {
     class AnonymousClass$0
     {
     public:
      AnonymousClass$0()  { }
      operator int (__cdecl *)(int k) { return cdecl_static_function; }
      operator int (__stdcall *)(int k) { return stdcall_static_function; }
      operator int (__fastcall *)(int k) { return fastcall_static_function; }
      int operator(int k) { return cdecl_static_function(k); }
     private:
      static int __cdecl cdecl_static_function(int k) { return calc(k + 42); }
      static int __stdcall stdcall_static_function(int k) { return calc(k + 42); }
      static int __fastcall fastcall_static_function(int k) { return calc(k + 42); }
     };
    
     auto f = AnonymousClass$0();
     ...
    }
    

    In other words, the compiler creates all the conversions, just in case. (The versions you don't use will be removed by the linker.)

    But only for noncapturing lambdas.

  • The Old New Thing

    What's the point of using a custom timer queue if there is a default one already?

    • 4 Comments

    A customer observed that when you create a timer via Create­Timer­Queue­Timer, you have the option of passing either NULL to get the default timer queue or a handle to a custom timer queue created by the Create­Timer­Queue function. Why would you want to create a custom timer queue?

    If you create a custom timer queue, then you know that all the timers in the queue are yours. For example, there might be another component in the process that creates a lot of timers in the default timer queue. If you had put your timers in the default timer queue, then your timers will risk starvation.

    It's the same sort of logic that might lead you to create a custom heap instead of using the default heap: You create a custom heap if you want to isolate your allocations from other components. Another component that creates a lot of allocations in the default heap may create fragmentation pressure in the default heap, but your custom heap is unaffected.

    Note, however, that in both cases (timer starvation and heap fragmentation), this is not something you typically worry about because the cost of the insurance often doesn't outweigh the benefit. For example, if you create your own timer queue, then the threads associated with your timer queue cannot be used to service timers from the default timer queue and vice versa, resulting in more active threads in the process than necessary, which increases scheduler overhead and memory usage. Since the threads from the two timer queues are not coordinating their efforts (since that's the point of creating a private timer queue), you can end up with CPU thrashing if both timer queues have collectively created more threads than there are CPU cores, and they are all running.

    After all, the whole point of a timer queue in the first place is to consolidate a lot of little pieces of work into a small number of threads. Creating a private timer queue is moving in the opposite direction.

    But if you want to do it, then the facility is there.

    For example, if you create a private heap, then you can free all the allocations in that heap by destroying the heap. Similarly, having a private timer queue means that you can cancel and delete all the timers by deleting the timer queue. Though at least in the timer case, you can get the best of both worlds (shared timer queue plus bulk cancellation) by associating all the timers with a cleanup group.

Page 7 of 450 (4,494 items) «56789»