November, 2012

  • The Old New Thing

    What does the COINIT_SPEED_OVER_MEMORY flag to CoInitializeEx do?

    • 20 Comments

    One of the flags you can pass to Co­Initialize­Ex is COINIT_SPEED_OVER_MEMORY, which is documented as

    COINIT_SPEED_OVER_MEMORY: Trade memory for speed.

    This documentation is already vague since it doesn't say which direction the trade is being made. Are you reducing memory to increase speed, or increasing memory by reducing speed? Actually it's neither: If you pass this flag, then you are instructing COM to consume more memory in an attempt to reduce CPU usage, under the assumption that you run faster by executing fewer cycles.¹

    The request is a per-process one-way transition. Once anybody anywhere in the process puts COM into speed-over-memory mode, the flag stays set and remains set until the process exits.

    When should you enable this mode? It doesn't matter, because as far as I can tell, there is no code anywhere in COM that changes its behavior based on whether the process has been placed into this mode! It looks like the flag was added when DCOM was introduced, but it never got hooked up to anything. (Or whatever code that had been hooked up to it never shipped.)

    ¹ As you know, consuming more memory is not a guarantee that you will actually run faster, because higher memory usage increases the chances that what you need will take an L1 cache miss or a page fault, which will cost you dearly in wait time (though not in CPU usage).

  • The Old New Thing

    Instead of trying to create a filter that includes everything, try just omitting the filter

    • 12 Comments

    The question was sent to a peer-to-peer discussion group for an internal program, let's call it Program Q. I'll add additional context so you can follow along.

    Hi, I'm trying to build a query that finds all issues owned by a particular user, regardless of which product the issue belongs to. I know that I can query for specific products by saying

    q select -owner bob -product LitWare
    q select -owner bob -product Contoso
    

    Is there a better way to do this than just running the query for every product in the database? It would be great to find all the issues at one shot instead of having to issue dozens of commands and combine the results.

    The person who submitted this question got so distracted by the -product filter, that they forgot that they could just omit the filter.

    q select -owner bob
    

    If you don't filter by product, then it finds everything regardless of the product.

    Enumerating all the products, then repeating the query for each product is some sort of anti-pattern. I don't know if it has a name, so I'll make one up. The long division anti-pattern performs an operation on a collection by arbitrarily breaking the collection into groups, then performing the operation on each member of each group, all this even though the grouping is unrelated to the operation.

    In C#, it would be phrased something like this:

    public void MakeUnavailable(string productId)
    {
        var stores = Inventory.Select(p => p.Store).Distinct();
    
        foreach (var store in stores) {
            foreach (var product in
                     from p in Inventory
                     where p.Store == store &&
                           p.ProductId == productId) {
                product.Available = false;
            }
        }
    }
    

    In words, we first dig through the inventory and collect all the unique stores. For each store, we go through the inventory again, looking for products from that store, and if the product is the one we want to make unavailable, set the Available property to false. (To avoid playing favorites, I used both fluent and query expression syntax.)

    Assuming the order in which the product is made unavailable is not important (and it doesn't appear to be, since we didn't sort the stores), the grouping by store is superfluous. You can just iterate across the entire inventory without regard for store:

    public void MakeUnavailable(string productId)
    {
        foreach (var product in
                 from p in Inventory
                 where p.ProductId == productId
                 select p) {
            product.Available = false;
        }
    }
    
  • The Old New Thing

    If you're asking somebody to help you, you want to make it easy for them, not harder

    • 36 Comments

    A customer liaison asked a question that went roughly like this:

    From: Liaison

    My customer is having a problem with the header file winfoo.h. The environment is Windows Vista German SP1 with Visual Studio 2008 Professional German SP1 and Windows SDK 6000.0.6000 German. When they try to include the file in their project, they get an error on line 42. "The character ';' was not expected here." Can somebody help?

    From: Raymond

    Can somebody please attach a copy of the offending file? There are many versions of winfoo.h, and it's not clear which version comes with Visual Studio 2008 Professional German SP1 + Windows SDK 6000.0.6000 German.

    I figured that'd be easier than provisioning a new virtual machine, obtaining temporary license keys for all the products, then installing Windows Vista German SP1, Visual Studio 2008 Professional German SP1, and Windows SDK 6000.0.6000 German. All to get one file. A file the customer already has right in front of them. Where all that's really interesting is line 42, and maybe a few lines surrounding it on either side.

    Time passes.

    The following day, another message arrives from the customer liaison.

    From: Liaison

    Anyone?

    At this point, I engaged my thermonuclear social skills.

    From: Raymond

    By "somebody", I meant "you".

    That went into some people's "Raymond Quotes" file.

    Remember, if you're asking somebody to help you, you want to make it easy for them, not harder.

    "Hey, Bob, I've got a jar of pickles, and I can't open the lid. Can you help?"

    Bob says, "Sure, I'll give it a try."

    "Great! The jar is in the storage room in the basement, on the third aisle, top shelf, second from the left."

    Bob is now suddenly less interested in helping you open that jar of pickles. Shouldn't you go get the jar of pickles and bring it to him?

  • The Old New Thing

    How do I get the tabbed dialog effect on my own custom tabbed dialog?

    • 13 Comments

    CJ observed that the standard tabbed dialogs provide an effect on the tab pages and want to know how to get the tabbed dialog effect on custom dialogs. fds242 noted this as well and wanted to know why the automatic tabbed dialog effect doesn't kick in until you put a static control on the child dialog box.

    Let's look at the first question first. To get the tabbed dialog effect, you can call Enable­Theme­Dialog­Texture with the ETDT_ENABLE­TAB flag. The best time to do this is in the WM_INIT­DIALOG handler, but you can also do it immediately after the dialog has been created. (Basically, you want to do this before the dialog paints for the first time, so as to avoid flicker.)

    Here's a sample program that shows a dummy dialog with the tabbed dialog texture enabled.

    // Hereby incorporated by reference:
    // dialog template and DlgProc function from this sample program
    // Comctl32 version 6 manifest from this sample program
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
        return DialogBox(hinst, MAKEINTRESOURCE(1), 0, DlgProc);
    }
    

    If you run this program, you get the expected dialog box without the tabbed dialog effect. But you can turn on the effect by calling the Enable­Theme­Dialog­Texture function:

    #include <uxtheme.h>
    
     case WM_INITDIALOG:
      CheckRadioButton(hdlg, 100, 102, 100);
      EnableThemeDialogTexture(hdlg, ETDT_ENABLETAB);
      return TRUE;
    

    Now, when you run the program, you get the tabbed dialog effect. It looks kind of weird when it's not in a tabbed dialog, but presumably you're going to put this dialog inside your own custom tabbed dialog control, so everything will look right when it's all finished.

    Now the second half of the question: Why doesn't the automatic tabbed dialog effect kick in until you put a static control on the child dialog box?

    If you look closely at the ETDT_ENABLE­TAB flag, you'll see that it's really two flags: ETDT_USE­TAB­TEXTURE and ETDT_ENABLE. The first flag says, "I would like to get the tab texture, if enabled"; the second flag says "Enable it." In other words, in order to get the tab texture, the tab texture needs to be both used and enabled.

    Originally, ETDT_ENABLE­TAB was just a single bit. Setting the bit turned on the tab texture. But it turns out that some programs didn't look good with the tab texture, and the common reason was that they created a dialog with no standard controls at all and then did custom drawing all over it. Therefore, the algorithm for enabling the tab texture was changed to the two-step version. The property sheet manager turned on the ETDT_USE­TAB­TEXTURE flag, and the button and static controls turned on the ETDT_ENABLE flag. Therefore, if your property sheet page has a button or a static, the second bit got turned on, and the tab texture became visible. On the other hand, if you didn't have any buttons or statics, then the assumption is that you're one of those programs that does custom dialog drawing, and the tab texture remains disabled.

    Let's watch it in action:

    1 DIALOGEX 32, 32, 200, 76
    STYLE DS_MODALFRAME
    CAPTION "Sample"
    FONT 8, "MS Shell Dlg"
    BEGIN
        // nothing!
    END
    
    INT_PTR CALLBACK DlgProc(HWND hdlg, UINT uMsg,
                             WPARAM wParam, LPARAM lParam)
    {
     switch (uMsg) {
     case WM_INITDIALOG:
      return TRUE;
     }
     return FALSE;
    }
    
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
     PROPSHEETPAGE psp = { sizeof(psp) };
     psp.hInstance = hinst;
     psp.pszTemplate = MAKEINTRESOURCE(1);
     psp.pfnDlgProc = DlgProc2;
    
     PROPSHEETHEADER psh = { sizeof(psh) };
     psh.dwFlags = PSH_PROPSHEETPAGE;
     psh.nPages = 1;
     psh.ppsp = &psp;
     return PropertySheet(&psh);
    }
    

    If you run this program, you'll see that there is no tabbed dialog texture. As we saw earlier, the reason there is no tabbed dialog texture is that the system is afraid that you're one of those programs that custom-draws their dialog boxes, so it's staying out of your way.

    But add this line:

     case WM_INITDIALOG:
      EnableThemeDialogTexture(hdlg, ETDT_ENABLETAB);
      return TRUE;
    

    The property sheet manager was afraid to give you that texture by default, but adding that line just adds the texture manually.

    This time, when you run the program, you get the happy tabbed dialog texture because you added it explicitly.

    I will leave you to answer fds242's final question: "Why do Windows Task Manager's tab pages still have the gray background."

  • The Old New Thing

    How do I parse a string into a FILETIME?

    • 5 Comments

    Public Service Announcement: Daylight Saving Time ends in most parts of the United States this weekend. Other parts of the world may change on a different day from the United States.

    The NLS functions in Win32 provide functions to convert a SYSTEMTIME into a string, but it does not provide any functions to perform the reverse conversion. Here are few things you can try:

    The OLE automation VarDateFromStr conversion function converts a string into a DATE. From there, you can convert it to some other format.

    BOOL SystemTimeFromStr(__in LPCWSTR psz, LCID lcid, __out LPSYSTEMTIME pst)
    {
      DATE date;
      return SUCCEEDED(VarDateFromStr(psz, lcid, 0, &date)) &&
             VariantTimeToSystemTime(date, pst);
    }
    
    BOOL FileTimeFromStr(__in LPCWSTR psz, LCID lcid, __out LPFILETIME pft)
    {
      SYSTEMTIME st;
      return SystemTimeFromStr(psz, lcid, &st) &&
             SystemTimeToFileTime(&st, pft);
    }
    

    If you have something in which parses CIM datetime format (which The Scripting Guys liken to Klingon) you can use the SWbemDateTime object. Since this is a scripting object, using it from C++ is rather cumbersome.

    BOOL FileTimeFromCIMDateTime(__in LPCWSTR psz, __out LPFILETIME pft)
    {
     BOOL fSuccess = FALSE;
     ISWbemDateTime *pDateTime;
     HRESULT hr = CoCreateInstance(__uuidof(SWbemDateTime), 0,
                     CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDateTime));
     if (SUCCEEDED(hr)) {
      BSTR bstr = SysAllocString(psz);
      if (bstr) {
       hr = pDateTime->put_Value(bstr);
       if (SUCCEEDED(hr)) {
        BSTR bstrFT;
        hr = pDateTime->GetFileTime(VARIANT_FALSE, &bstrFT);
        if (SUCCEEDED(hr)) {
         __int64 i64FT = _wtoi64(bstrFT);
         pft->dwLowDateTime = LODWORD(i64FT);
         pft->dwHighDateTime = HIDWORD(i64FT);
         fSuccess = TRUE;
         SysFreeString(bstrFT);
        }
       }
       SysFreeString(bstr);
      }
      pDateTime->Release();
     }
     return fSuccess;
    }
    

    From the managed side, you have Date­Time.Try­Parse and Date­Time.Parse­Exact methods.

    I leave you to investigate the time zone and locale issues associated with these techniques. (Because I can't be bothered.)

  • The Old New Thing

    When you synthesize input with SendInput, you are also synthesizing the timestamp

    • 6 Comments

    A customer was reporting a problem when they used the Send­Input function to simulate a drag/drop operation for automated testing purposes.

    I see the mouse move from one location to another, and the starting and stopping locations are correct on the screen, but the mouse moves instantaneously rather than waiting 500ms between operations. Here's how I'm sending the input.

    INPUT input[3] = { 0 };
    
    // Click
    input[0].type = INPUT_MOUSE;
    input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    input[0].mi.time = 500;
    
    // Drag
    input[1].type = INPUT_MOUSE;
    input[1].mi.dwFlags = MOUSEEVENTF_MOVE;
    input[1].mi.dx = 100;
    input[1].mi.dy = 100;
    input[1].mi.time = 1000;
    
    // Release
    input[2].type = INPUT_MOUSE;
    input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
    input[2].mi.time = 500;
    
    SendInput(3, input, sizeof(INPUT));
    

    Well, yeah, all the events occur immediately because you submitted them all at once.

    The time field in the MOUSE­INPUT structure is not for introducing delays in playback. Though I'm not sure what the customer thought the time field was. They say that they want a 500ms delay between operations. At first, I thought that they may have misinterpreted it as a delay relative to the time the Send­Input call is made, since they set input[0].mi.time to 500 and input[1].mi.time to 1000. But if thay were the case, then setting input[2].mi.time to 500 would end up going backward in time. But looking at the big picture, it's probably not worth trying to figure out what they were thinking, since that code will have to be scrapped anyway.

    The time field is for letting an input source (typically a hardware device) say, "Hi, um, the mouse left button went down at 9:30 this morning. Yes, I know it's already 10am. The PCI bus got a flat tire, and then the spare was also flat, and really there's no point going into the details. Sorry this message arrived late." The window manager (and anybody else who bothers to check the time member of the MSG structure) uses this information to do things like detect double-clicks. If the input source later reports, "Hi, um, the mouse left button went up at 9:30:00.100 this morning, sorry for the late report," the window manager says, "Well, that was only 100 milliseconds after the button went down thirty minutes ago, so I guess that's a double-click after all. Could you try to be a bit more prompt with this information in the future?" (Sarcasm added.)

    In other words, the time member of the MOUSE­INPUT structure is for backdating input events. They still get delivered immediately, but the timestamp allows the window manager (and other code which looks at the timestamp) to make decisions about how they should respond.

    Note that post-dating the timestamp does not cause the input delivery to be delayed, and back-dating the timestamp does not cause the input to be inserted into the input stream ahead of other input. The input is merely delivered with a timestamp in the future or in the past. (And who knows what sort of havoc that will create if a program checks the timestamps and notices that they are either from the future or have traveled back in time. Maybe you'll get a call from Microsoft Research asking for more information about your time machine.)

    If you want three input events to take place with a 500ms delay between them, then you need to call Send­Input three times, with a 500ms delay between the calls.

Page 3 of 3 (26 items) 123