• The Old New Thing

    Some light reading on lock-free programming

    • 6 Comments

    Today is a holiday in the United States, so I'm going to celebrate by referring you to other things to read.

    I'm going to start with a presentation by Bruce Dawson at GDC 2009, which is basically multiple instances of the question "Is this code correct?", and the answer is always "No!" Although the title of the talk is Lockless Programming in Games, the information is relevant to pretty much everybody. I can't find a recording of the presentation, but you can download the PowerPoint slides or view them in your browser. But I recommend downloading the PowerPoint slides and reading the notes, because the notes explain the slides. [Update: Ah, you can see the notes in the browser by clicking the Notes button at the bottom. So download whichever you prefer. Just make sure you read the notes.]

    A more game-focused presentation by Bruce Dawson has the more general title Coding for Multiple Cores. Download the PowerPoint sides or view them in your browser.

    Then there is the MSDN white paper that he authored, Lockless Programming Considerations for Xbox 360 and Microsoft Windows.

    Finally, there's Herb Sutter's two-part talk atomic<> Weapons, part 1 and part 2.

    That should keep you busy for a while.

  • The Old New Thing

    If 16-bit Windows had a single input queue, how did you debug applications on it?

    • 29 Comments

    After learning about the bad things that happened if you synchronized your application's input queue with its debugger, commenter kme wonders how debugging worked in 16-bit Windows, since 16-bit Windows didn't have asynchronous input? In 16-bit Windows, all applications shared the same input queue, which means you were permanently in the situation described in the original article, where the application and its debugger (and everything else) shared an input queue and therefore would constantly deadlock.

    The solution to UI deadlocks is to make sure the debugger doesn't have any UI.

    At the most basic level, the debugger communicated with the developer through the serial port. You connected a dumb terminal to the other end of the serial port. Mine was a Wyse 50 serial console terminal. All your debugging happened on the terminal. You could disassemble code, inspect and modify registers and memory, and even patch new code on the fly. If you wanted to consult source code, you needed to have a copy of it available somewhere else (like on your other computer). It was similar to using the cdb debugger, where the only commands available were r, db, eb, u, and a. Oh, and bp to set breakpoints.

    Now, if you were clever, you could use a terminal emulator program so you didn't need a dedicated physical terminal to do your debugging. You could connect the target computer to your development machine and view the disassembly and the source code on the same screen. But you weren't completely out of the woods, because what did you use to debug your development machine if it crashed? The dumb terminal, of course.¹

    Target machine
    Debugger
    Development machine
    Debugger
    Wyse 50
    dumb terminal

    I did pretty much all my Windows 95 debugging this way.

    If you didn't have two computers, another solution was to use a debugger like CodeView. CodeView avoided the UI deadlock problem by not using the GUI to present its UI. When you hit a breakpoint or otherwise halted execution of your application, CodeView talked directly to the video driver to save the first 4KB of video memory, then switched into text mode to tell you what happened. When you resumed execution, it restored the video memory, then switched the video card back into graphics mode, restored all the pixels it captured, then resumed execution as if nothing had happened. (If you were debugging a graphics problem, you could hit F3 to switch temporarily to graphics mode, so you could see what was on the screen.)

    If you were really fancy, you could spring for a monochrome adapter, either the original IBM one or the Hercules version, and tell CodeView to use that adapter for its debugging UI. That way, when you broke into the debugger, you could still see what was on the screen! We had multiple monitors before it was cool.

    ¹ Some people were crazy and cross-connected their target and development machines.

    Target machine
    Debugger
    Development machine
    Debugger

    This allowed them to use their target machine to debug their development machine and vice versa. But if your development machine crashed while it was debugging the target machine, then you were screwed.

  • The Old New Thing

    What is the difference between Full Windows Touch Support and Limited Touch Support?

    • 19 Comments

    In the System control panel and in the PC Info section of the PC & Devices section of PC Settings, your device's pen and touch support can be reported in a variety of ways. Here is the matrix:

    No pen Pen
    No touch No Pen or Touch Input Pen Support
    Single touch Single Touch Support Pen and Single Touch Support
    Limited multi-touch Limited Touch Support with N Touch Points Pen and Limited Touch Support with N Touch Points
    Full multi-touch Full Touch Support with N Touch Points Pen and Full Touch Support with N Touch Points

    The meaning of No touch and Single touch are clear, but if a device supports multiple touch points, what makes the system report it as having Limited versus Full touch support?

    A device with Full touch support is one that has passed Touch Hardware Quality Assurance (THQA). You can read about the Windows Touch Test Lab (WTTL) to see some of the requirements for full touch support.

    If you have a touch device without full touch support, then Windows will lower its expectations from the device. For example, it will not use the timestamps on the device packets, and it will increase the tolerances for edge gestures.

    Note that if test signing is enabled, then all multitouch drivers are treated as having full touch support. (This lets you test your driver in Full mode before submitting it to THQA.)

  • The Old New Thing

    The crazy world of stripping diacritics

    • 25 Comments

    Today's Little Program strips diacritics from a Unicode string. Why? Hey, I said that Little Programs require little to no motivation. It might come in handy in a spam filter, since it was popular, at least for a time, to put random accent marks on spam subject lines in order to sneak past keyword filters. (It doesn't seem to be popular any more.)

    This is basically a C-ization of the C# code originally written by Michael Kaplan. Don't forget to read the follow-up discussion that notes that this can result in strange results.

    First, let's create our dialog box. Note that I intentionally give it a huge font so that the diacritics are easier to see.

    // scratch.h
    
    #define IDD_SCRATCH 1
    #define IDC_SOURCE 100
    #define IDC_SOURCEPOINTS 101
    #define IDC_DEST 102
    #define IDC_DESTPOINTS 103
    
    // scratch.rc
    
    #include <windows.h>
    #include "scratch.h"
    
    IDD_SCRATCH DIALOGEX 0, 0, 320, 88
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
    Caption "Stripping diacritics"
    FONT 20, "MS Shell Dlg"
    BEGIN
        LTEXT "Original:", -1, 4, 8, 38, 10
        EDITTEXT IDC_SOURCE, 46, 6, 270, 12, ES_AUTOHSCROLL
        LTEXT "", IDC_SOURCEPOINTS, 46, 22, 270, 12
        LTEXT "Modified:", -1, 4, 40, 38, 10
        EDITTEXT IDC_DEST, 46, 38, 270, 12, ES_AUTOHSCROLL
        LTEXT "", IDC_DESTPOINTS, 46, 54, 270, 12
        DEFPUSHBUTTON "OK", IDOK, 266, 70, 50, 14
    END
    

    Now the program that uses the dialog box.

    // scratch.cpp
    
    #define STRICT
    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <windowsx.h>
    #include <strsafe.h>
    #include "scratch.h"
    
    #define MAXSOURCE 64
    
    void SetDlgItemCodePoints(HWND hwnd, int idc, PCWSTR psz)
    {
      wchar_t szResult[MAXSOURCE * 4 * 5];
      szResult[0] = 0;
      PWSTR pszResult = szResult;
      size_t cchResult = ARRAYSIZE(szResult);
      HRESULT hr = S_OK;
      for (; SUCCEEDED(hr) && *psz; psz++) {
        wchar_t szPoint[6];
        hr = StringCchPrintf(szPoint, ARRAYSIZE(szPoint), L"%04x ", *psz);
        if (SUCCEEDED(hr)) {
          hr = StringCchCatEx(pszResult, cchResult, szPoint, &pszResult, &cchResult, 0);
        }
      }
      SetDlgItemText(hwnd, idc, szResult);
    }
    

    The Set­Dlg­Item­Code­Points function takes a UTF-16 string and prints all the code points. This is just to help visualize the result; it's not part of the actual diacritic-removal algorithm.

    void OnUpdate(HWND hwnd)
    {
      wchar_t szSource[MAXSOURCE];
      GetDlgItemText(hwnd, IDC_SOURCE, szSource, ARRAYSIZE(szSource));
      wchar_t szDest[MAXSOURCE * 4];
    
      int cchActual = NormalizeString(NormalizationKD,
                                      szSource, -1,
                                      szDest, ARRAYSIZE(szDest));
      if (cchActual <= 0) szDest[0] = 0;
    
      WORD rgType[ARRAYSIZE(szDest)];
      GetStringTypeW(CT_CTYPE3, szDest, -1, rgType);
    
      PWSTR pszWrite = szDest;
      for (int i = 0; szDest[i]; i++) {
        if (!(rgType[i] & C3_NONSPACING)) {
          *pszWrite++ = szDest[i];
        }
      }
      *pszWrite = 0;
    
      SetDlgItemText(hwnd, IDC_DEST, szDest);
      SetDlgItemCodePoints(hwnd, IDC_SOURCEPOINTS, szSource);
      SetDlgItemCodePoints(hwnd, IDC_DESTPOINTS, szDest);
    }
    

    Okay, here's where the actual work happens. We put the source string into Normalization Form KD. This decomposes the diacritics so that we can identify them with Get­String­TypeW and then strip them out.

    Of course, in real life, you wouldn't hard-code the array sizes like I did here, but this is just a Little Program, and Little Programs are allowed to take shortcuts.

    The rest of the program is just a framework to get into that function.

    INT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm,
                             WPARAM wParam, LPARAM lParam)
    {
      switch (wm)
      {
      case WM_INITDIALOG:
        return TRUE;
    
      case WM_COMMAND:
        switch (GET_WM_COMMAND_ID(wParam, lParam)) {
        case IDC_SOURCE:
          switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
        case EN_UPDATE:
          OnUpdate(hwnd);
          break;
        }
        break;
        case IDOK:
          EndDialog(hwnd, 0);
          return TRUE;
      }
      break;
    
      case WM_CLOSE:
        EndDialog(hwnd, 0);
        return TRUE;
      }
    
      return FALSE;
    }
    
    int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPWSTR lpCmdLine, int nShowCmd)
    {
      DialogBox(hinst, MAKEINTRESOURCE(IDD_SCRATCH), nullptr, DlgProc);
      return 0;
    }
    

    Okay, let's take this program for a spin. Here are some interesting characters to try:

    Original character Resulting character
    ª 00AA Feminine ordinal indicator a 0061 Latin small letter a
    ¹ 00B1 Superscript one 1 0031 Digit one
    ½ 00BD Vulgar fraction one half 1⁄2 0031 2044 0032 Digit one + Fraction slash + Digit two
    ı 0131 Latin small letter dotless i ı 0131 Latin small letter dotless i
    Ø 00D8 Latin capital letter O with stroke Disappears!
    ł 0142 Latin small letter l with stroke ł 0142 Latin small letter l with stroke
    ŀ 0140 Latin small letter l with middle dot 006C 00B7 Latin small letter l + middle dot
    æ 00E6 Latin small letter ae æ 00E6 Latin small letter ae
    Ή 0389 Greek capital letter Eta with tonos Η 0397 Greek capital letter Eta
    А 0410 Cyrillic capital letter А А 0410 Cyrillic capital letter А
    Å 00C5 Latin capital letter A with ring above A 0041 Latin capital letter A
    FF21 Fullwidth Latin capital letter A A 0041 Latin capital letter A
    2460 Circled digit one 1 0031 Digit one
    2780 Dingbat circled sans-serif digit one 2780 Dingbat circled sans-serif digit one
    ® 00AE Registered sign ® 00AE Registered sign
    24c7 Circled Latin capital letter R R 0052 Latin capital letter R
    𝖕 D835 DD95 Mathematical bold Fraktur small p p 0070 Latin small letter p
    FF6C Halfwidth Katakana letter small Ya 30E3 Katakana letter small Ya
    30E3 Katakana letter small Ya 30E3 Katakana letter small Ya
    30B4 Katakana letter Go 30B3 Katakana letter Ko
    201C Left double quotation mark 201C Left double quotation mark
    201D Right double quotation mark 201D Right double quotation mark
    201E Double low-9 quotation mark 201E Double low-9 quotation mark
    201F Double high-reversed-9 quotation mark 201F Double high-reversed-9 quotation mark
    2033 Double prime ′′ 2032 2032 Prime + Prime
    2035 Reverse prime 2035 Reverse prime
    2039 Single left-pointing angle quotation mark 2039 Single left-pointing angle quotation mark
    « 00AB Left-pointing double angle quotation mark « 00AB Left-pointing double angle quotation mark
    2014 Em-dash 2014 Em-dash
    203C Double exclamation mark !! 0021 0021 Exclamation mark + Exclamation mark

    There are some interesting quirks here. Mind you, this is what the Unicode Consortium says, so if you think they are wrong, you can take it up with them.

    The superscript-like characters are converted to their plain versions. Enclosed alphabetics are also converted, but not the ® symbol. Fullwidth forms of Latin letters are converted to their halfwidth equivalents. On the other hand, halfwidth Katakana characters are expanded to their fullwidth equivalents. But small Katakana does not convert to their large equivalents.

    The Ø disappears completely! What's up with that? The character code for Ø is reported as C3_ALPHA | C3_NONSPACING | C3_DIACRITIC, and since we are removing nonspacing characters, this causes it to be removed. (Why is Ø nonspacing? It occupies space!) For whatever reason, it does not decompose into O + Combining Solidus Overlay. On the other hand, the Polish ł remains intact because it is reported as C3_ALPHA | C3_DIACRITIC. Poland wins and Norway loses?

    The diacritic removal ignores linguistic rules. The Swedish Å decomposes into a capital A and a combining ring above, even though in Swedish, the character is considered nondecomposable. (Just like the capital letter Q in English does not decompose into an O and a tail.) Katakana Go suffers a similar ignoble fate, converting to Katakana Ko, which is linguistically nonsensical. But then again, removing diacritics is already linguistically nonsensical. Nonsensical operation is nonsensical.

    There is no attempt to unify look-alike characters from different scripts. Look-alike characters in the Greek and Cyrillic alphabets are not mapped to their Latin doppelgängers.

    The infamous Turkish dotless i does not turn into a dotted i. (And the lowercase Latin i does not decompose into a combining dot and a dotless i.)

    Finally, I tried a selection of punctuation marks. Most of them pass through unchanged, with the exception of the double prime and double exclamation mark which each decompose into a pair of singles. (But double quotation marks do not decompose into a pair of singles.)

    Okay, but the goal of this exercise was spam detection, so we are actually interested in mapping as far as possible all the way down to plain ASCII. We'd like to convert, for example, the look-alike characters in the Cyrillic and Greek alphabets to the Latin characters they resemble.

    So let's try something else. If we want to convert to ASCII, then just convert to ASCII!

    #define CP_ASCII 20127
    void OnUpdate(HWND hwnd)
    {
      wchar_t szSource[MAXSOURCE];
      GetDlgItemText(hwnd, IDC_SOURCE, szSource, ARRAYSIZE(szSource));
      char szDest[MAXSOURCE * 2];
      int cchActual = WideCharToMultiByte(CP_ASCII, 0, szSource, -1,
                                  szDest, ARRAYSIZE(szDest), 0, 0);
      if (cchActual <= 0) szDest[0] = 0;
    
      SetDlgItemTextA(hwnd, IDC_DEST, szDest);
      SetDlgItemCodePoints(hwnd, IDC_SOURCEPOINTS, szSource);
    }
    

    We can extend the table above with a new column.

    Original character KD character ASCII character
    ª 00AA Feminine ordinal indicator a 0061 Latin small letter a a 0061 Latin small letter a
    ¹ 00B1 Superscript one 1 0031 Digit one 1 0031 Digit one
    ½ 00BD Vulgar fraction one half 1⁄2 0031 2044 0032 Digit one + Fraction slash + Digit two ? No conversion
    ı 0131 Latin small letter dotless i ı 0131 Latin small letter dotless i i 0069 Latin small letter i
    Ø 00D8 Latin capital letter O with stroke Disappears! O 004F Latin capital letter O
    ł 0142 Latin small letter l with stroke ł 0142 Latin small letter l with stroke l 006C Latin small letter l
    ŀ 0140 Latin small letter l with middle dot 006C 00B7 Latin small letter l + middle dot ? No conversion
    æ 00E6 Latin small letter ae æ 00E6 Latin small letter ae a 0061 Latin small letter a
    Ή 0389 Greek capital letter Eta with tonos Η 0397 Greek capital letter Eta ? No conversion
    А 0410 Cyrillic capital letter А А 0410 Cyrillic capital letter А ? No conversion
    Å 00C5 Latin capital letter A with ring above A 0041 Latin capital letter A A 0041 Latin capital letter A
    FF21 Fullwidth Latin capital letter A A 0041 Latin capital letter A A 0041 Latin capital letter A
    2460 Circled digit one 1 0031 Digit one ? No conversion
    2780 Dingbat circled sans-serif digit one 2780 Dingbat circled sans-serif digit one ? No conversion
    ® 00AE Registered sign ® 00AE Registered sign R 0052 Latin capital letter R
    24c7 Circled Latin capital letter R R 0052 Latin capital letter R ? No conversion
    𝖕 D835 DD95 Mathematical bold Fraktur small p p 0070 Latin small letter p ?? No conversion
    FF6C Halfwidth Katakana letter small Ya 30E3 Katakana letter small Ya ? No conversion
    30E3 Katakana letter small Ya 30E3 Katakana letter small Ya ? No conversion
    30B4 Katakana letter Go 30B3 Katakana letter Ko ? No conversion
    201C Left double quotation mark 201C Left double quotation mark " 0022 Quotation mark
    201D Right double quotation mark 201D Right double quotation mark " 0022 Quotation mark
    201E Double low-9 quotation mark 201E Double low-9 quotation mark " 0022 Quotation mark
    201F Double high-reversed-9 quotation mark 201F Double high-reversed-9 quotation mark ? No conversion
    2033 Double prime ′′ 2032 2032 Prime + Prime ? No conversion
    2032 Prime 2032 Prime ' 0027 Apostrophe
    2035 Reverse prime 2035 Reverse prime ` 0060 Grave accent
    2039 Single left-pointing angle quotation mark 2039 Single left-pointing angle quotation mark < 003C Less-than sign
    « 00AB Left-pointing double angle quotation mark « 00AB Left-pointing double angle quotation mark < 003C Less-than sign
    2014 Em-dash 2014 Em-dash - 002D Hyphen-minus
    203C Double exclamation mark !! 0021 0021 Exclamation mark + Exclamation mark ? No conversion

    There are some interesting differences here.

    Some characters fail to convert to ASCII outright. This is not unexpected for the Japanese characters, is mildly unexpected for the look-alikes in the Cyrillic and Greek alphabets, and is surprising for some characters like double prime, double exclamation point, enclosed alphanumerics, and vulgar fractions because they had ASCII decompositions in Normalization Form KD, but converting directly into ASCII refused to use them.

    But the dotless i gets its dot back.

    Another weird thing you might notice is that the æ converts to just the a. This goes contrary to the expectations of American English, because words which historically use the æ and œ are largely respelled in American English to use just the e. (Encyclopædia → encyclopedia, fœtus → fetus.) Mysteries abound.

    If your real goal is to map every character to its nearest ASCII look-alike, then all these code page games are just beating around the bush. The way to go is to use the Unicode Confusables database. There is a huge data file and instructions on how to use it. There's also a nice Web site that lets you explore the confusables database interactively.

    Or you could just take the sledgehammer approach: If there are a significant number of characters outside the Latin alphabet and punctuation and you are expecting English text, then just reject it as likely spam.

    ಠ_ಠ

  • The Old New Thing

    Is it wrong to call SHFileOperation from a service? Revised

    • 27 Comments

    My initial reaction to this question was to say, "I don't know if I'd call it wrong, but I'd call it highly inadvisable."

    I'd like to revise my guidance.

    It's flat-out wrong, at least in the case where you call it while impersonating.

    The registry key HKEY_CURRENT_USER is bound to the current user at the time the key is first accessed by a process:

    The mapping between HKEY_CURRENT_USER and HKEY_USERS is per process and is established the first time the process references HKEY_CURRENT_USER. The mapping is based on the security context of the first thread to reference HKEY_CURRENT_USER. If this security context does not have a registry hive loaded in HKEY_USERS, the mapping is established with HKEY_USERS\.Default. After this mapping is established it persists, even if the security context of the thread changes.

    Emphasis mine.

    This means that if you impersonate a user, and then access HKEY_CURRENT_USER, then that binds HKEY_CURRENT_USER to the impersonated user. Even if you stop impersonating, future references to HKEY_CURRENT_USER will still refer to that user.

    This is probably not what you expected.

    The shell takes a lot of settings from the current user. If you impersonate a user and then call into the shell, your service is now using that user's settings, which is effectively an elevation of privilege: An unprivileged user is now modifying settings for a service. For example, if the user has customized the Print verb for text files, and you use Shell­Execute to invoke the print verb on a text document, you are at the mercy of whatever the user's print verb is bound to. Maybe it runs Notepad, but maybe it runs pwnz0rd.exe. You don't know.

    Similarly, the user might have a per-user registered copy hook or namespace extension, and now you just loaded a user-controlled COM object into your service.

    In both cases, this is known to insiders as hitting the jackpot.

    Okay, so what about if you call Shell­Execute or some other shell function while not impersonating? You might say, "That's okay, because the current user's registry is the service user, not the untrusted attacker user." But look at that sentence I highlighted up there. Once HKEY_CURRENT_USER get bound to a particular user, it remains bound to that user even after impersonation ends. If somebody else inadvisedly called a shell function while impersonating, and that shell function happens to be the first one to access HKEY_CURRENT_USER, then your call to a shell function while not impersonating will still use that impersonated user's registry. Congratulations, you are now running untrusted code, and you're not even impersonating any more!

    So my recommendation is don't do it. Don't call shell functions while impersonating unless the function is explicitly documented as supporting impersonation. (The only ones I'm aware of that fall into this category are functions like SHGet­Folder­Path which accept an explicit token handle.) Otherwise, you may have created (or in the case of copy hooks, definitely created) a code injection security vulnerability in your service.

  • The Old New Thing

    A library loaded via LOAD_LIBRARY_AS_DATAFILE (or similar flags) doesn't get to play in any reindeer module games

    • 23 Comments

    If you load a library with the LOAD_LIBRARY_AS_DATA­FILE flag, then it isn't really loaded in any normal sense. In fact, it's kept completely off the books.

    If you load a library with the LOAD_LIBRARY_AS_DATA­FILE, LOAD_LIBRARY_AS_DATA­FILE_EXCLUSIVE, or LOAD_LIBRARY_AS_IMAGE_RESOURCE flag (or any similar flag added in the future), then the library gets mapped into the process address space, but it is not a true module. Functions like Get­Module­Handle, Get­Module­File­Name, Enum­Process­Modules and Create­Toolhelp32­Snapshot will not see the library, because it was never entered into the database of loaded modules.

    These "load library as..." flags don't actually load the library in any meaningful sense. They just take the file and map it into memory manually without updating any module tracking databases. This functionality was overloaded into the Load­Library­Ex function, which in retrospect was probably not a good idea, because people expect Load­Library­Ex to create true modules, but these flags create pseudo-modules, a term I made up just now.

    It would have been less confusing in retrospect if the "load library as..." functionality were split into another function like Load­File­As­Pseudo­Module. Okay, that's a pretty awful name, but that's not the point. The point is to put the functionality in some function that doesn't have the word library in its name.

    Okay, so now that we see that these pseudo-modules aren't true modules, and they don't participate in any reindeer module games. So what use are they?

    Basically, the only thing you can do with a pseudo-module is access its resources with functions like Find­Resource, Load­Resource, and Enum­Resource­Types. Note that this indirectly includes functions like Load­String, and Format­Message which access resources behind the scenes.

    So maybe a better name for the function would have been Load­File­For­Resources, since that's all the pseudo-module is good for.

  • The Old New Thing

    Distinguishing between normative and positive statements to help people answer your question

    • 28 Comments

    Often, we get questions from a customer that use the word should in an ambiguous way:

    Our program creates a widget whose flux capacitor should have reverse polarity. Attached is a sample program that shows how we create the widget with Create­Widget. However, the resulting widget still has a flux capacitor with standard polarity. Can you help us?

    The phrase should have reverse polarity is ambiguous. The question could be

    We would like to create a widget whose flux capacitor has reverse polarity. Attached is a sample program that shows how to create a widget whose flux capacitor has standard polarity. How should we modify it in order to get reverse polarity?

    Or the question might be

    We would like to create a widget whose flux capacitor has reverse polarity. Attached is a sample program that attempts to do so, but the resulting widget has a flux capacitor with standard polarity. The polarity flag appears to be ignored. Are are we doing something wrong, or is this a bug in Windows?

    The first is a normative statement: "This is what we would like to happen." The second is a positive statement: "This is what is happening."

    The distinction is important because the two types of statements require very different types of responses. If have a program that does X, and you want to change it to do Y, then you're asking for help working through the Y feature, clarifying the documentation, informing you which flags you need to pass, and so on. But if you have a program that tries to do Y and fails, then you're asking for help debugging your code and possibly identifying a bug in the operating system.

    Being clear with your request means that you can avoid wasting a lot of time when the wrong set of people are called in to help you out.

    Here's another example of vague use of the word should:

    We're trying to do XYZ. We've been told that it is blocked for security reasons, but there should be a way to do this.

    In this case, it is not clear what the customer means by the phrase should be a way to do this. It could be

    We're trying to do XYZ. We've been told that it is blocked for security reasons, but we think that Windows should be changed to allow our scenario. How can we file a change request with the Windows security team to make an exception for us?

    Or the customer might be trying to say

    We're trying to do XYZ. We've been told that it is blocked for security reasons, but we think that there is a way to get the effect of XYZ without triggering the security issue. Can you help us find it?

    Note that in both cases, the customer either failed to asked a question or made some statements and asked for nonspecific advice, which is effectly the same as not asking a question. If they had remembered to ask a question, then that question would have clarified what they intended by the word should.

    Bonus chatter: A physicist classmate of mine got a chuckle out of the phrase flux capacitor because it combines two physics terms in an impressive-sounding but mostly nonsensical way.

    A capacitor is a device which stores electric potential. In the hydraulic analogy of electricity, a capacitor is a rubber diaphragm that separates two parts of a pipe, but which "stores" water flow by stretching and "discharges" the water flow by returning to its rest position.

    Flux is cross-sectional flow per unit time. Water flux is volumetric flow rate (liters per second per square meter): it measures how vigorously the water flows across a boundary. Magnetic flux measures the strength of a magnetic field.

    The combination is nonsensical because the units don't match. A capacitor stores potential, whereas flux is measured in current or magnetic field strength. But if you generalize the term capacitor to mean "a thing that stores stuff", then a flux capacitor is a device which stores a magnetic field.

    Such devices already exist today. They are called magnets.

  • The Old New Thing

    File version information does not appear in the property sheet for some files

    • 26 Comments

    A customer reported that file version information does not appear on the Details page of the property sheet which appears when you right-click the file and select Properties. They reported that the problem began in Windows 7.

    The reason that the file version information was not appearing is that the file's extension was .xyz. Older versions of Windows attempted to extract file version information for all files regardless of type. I believe it was Windows Vista that changed this behavior and extracted version information only for known file types for Win32 modules, specifically .cpl, .dll, .exe, .ocx, .rll, and .sys. If the file's extension is not on the list above, then the shell will not sniff for version information.

    If you want to register a file type as eligible for file version extraction, you can add the following registry key:

    HKEY_LOCAL_MACHINE
     \Software
      \Microsoft
        \Windows
          \CurrentVersion
            \PropertySystem
              \PropertyHandlers
                \.XYZ
                 (Default) = REG_SZ:"{66742402-F9B9-11D1-A202-0000F81FEDEE}"
    

    (Thanks in advance for complaining about this change in behavior. This always happens whenever I post in the Tips/Support category about how to deal with a bad situation. Maybe I should stop trying to explain how to deal with bad situations.)

  • The Old New Thing

    How do I enumerate drives the same way that the NET USE command does?

    • 14 Comments

    If you use the Remote Desktop Connection client to connect to another computer, you have the option of making your local drives available to the remote computer.

    A customer wanted to know how to enumerate all the drives on the local machine. The were able to get the volumes mapped to drive letters, but they also wanted to get the redirected drives injected by Terminal Services. (Mind you, these aren't volumes that are assigned drive letters, so it's not clear why they are interested in them, but whatever.)

    With the NET USE command, they see the Terminal Services volumes in Explorer, and they can be browsed via \\tsclient\d:

    Status       Local     Remote                    Network
    
    -------------------------------------------------------------------------------
                           \\TSCLIENT\D              Microsoft Terminal Services
    The command completed successfully.
    

    The customer wanted to enumerate these Terminal Services client volumes. "How does the NET USE command enumerate these volumes?"

    Let's write that program. Remember, Little Programs do little to no error checking.

    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <winnetwk.h>
    #include <stdio.h>
    
    void report(PCWSTR pszLabel, PCWSTR pszValue)
    {
     printf("%ls = %ls\n", pszLabel, pszValue ? pszValue : L"(null)");
    }
    
    int __cdecl main(int, char **)
    {
     HANDLE hEnum;
     WNetOpenEnum(RESOURCE_CONNECTED,
                  RESOURCETYPE_DISK,
                  0,
                  NULL,
                  &hEnum);
    
     DWORD cbBuffer = 65536;
     void *buffer = LocalAlloc(LMEM_FIXED, cbBuffer);
     LPNETRESOURCE pnr = (LPNETRESOURCE)buffer;
    
     DWORD err;
     do {
      DWORD cEntries = INFINITE;
      DWORD cb = cbBuffer;
      err = WNetEnumResource(hEnum, &cEntries, buffer, &cb);
      if (err == NO_ERROR || err == ERROR_MORE_DATA) {
       for (DWORD i = 0; i < cEntries; i++) {
        report(L"localName", pnr[i].lpLocalName);
        report(L"remoteName", pnr[i].lpRemoteName);
        report(L"provider", pnr[i].lpProvider);
        printf("\n");
       }
      }
     } while (err == ERROR_MORE_DATA);
    
     LocalFree(buffer);
     WNetCloseEnum(hEnum);
     return 0;
    }
    

    We open an enumeration for connected disks and then start enumerating out of it. The usage pattern for WNet­Enum­Resources is kind of messy, with a bunch of in/out parameters that need to get reset each time. Each time, we say "Enumerate as much as you can into this 64KB buffer" and then print what we got. If we were told, "There's still more," then we go back and ask for more.

    That's all. Nothing particularly fancy.

  • The Old New Thing

    How to view the stack of a user-mode thread when its kernel stack has been paged out

    • 4 Comments

    Suppose you have a machine that has crashed, and your investigation shows that the reason is that there is a critical section that everybody is waiting for. While waiting for that critical section, work piles up, and eventually the machine keels over. Suppose further that this crash is given to you in the form of a kernel debugger.

    In case it wasn't obvious, by "you" I mean "me".

    Okay, so the critical section that is the cause of the logjam is this one:

    1: kd> !cs CONTOSO!g_csDataLock
    -----------------------------------------
    Critical section   = 0x00007ff7f0ed2f68 (CONTOSO!g_csDataLock+0x0)
    DebugInfo          = 0x0000000022f2efd0
    LOCKED
    LockCount          = 0x5D
    WaiterWoken        = No
    OwningThread       = 0x0000000000004228
    RecursionCount     = 0x1
    LockSemaphore      = 0x17A0
    SpinCount          = 0x00000000020007cb
    

    "Great," you say. "I just need to look at thread 0x4228 to see why it is stuck.

    1: kd> !process -1 4
    PROCESS ffffe000047ae900
        SessionId: 1  Cid: 0604    Peb: 7ff74ecfa000  ParentCid: 05cc
        DirBase: 0eb07000  ObjectTable: ffffc000014c5680  HandleCount: 7003.
        Image: contoso.exe
    
            ...
            THREAD ffffe0000c136080  Cid 0604.4228  Teb: 00007ff74e94c000 Win32Thread: fffff90144edea60 WAIT
            ...
    

    Woo-hoo, there's the thread. Now I just need to switch to its context to see what it is stuck on.

    1: kd> .thread ffffe0000c136080
    Can't retrieve thread context, Win32 error 0n30
    

    Okay, that didn't work out too well. Now what?

    Even though the kernel stack is paged out, the user-mode stack may still be available.

    1: kd> !thread ffffe0000c136080
    THREAD ffffe0000c136080  Cid 0604.4228  Teb: 00007ff74e94c000
           Win32Thread: fffff90144edea60 WAIT: (UserRequest) UserMode Non-Alertable
        ffffe000077a7830  NotificationEvent
    Not impersonating
    DeviceMap                 ffffc00000e89c80
    Owning Process            ffffe000047ae900       Image:         contoso.exe
    Attached Process          N/A            Image:         N/A
    Wait Start TickCount      12735890       Ticks: 328715 (0:01:25:36.171)
    Context Switch Count      75             IdealProcessor: 2
    UserTime                  00:00:00.000
    KernelTime                00:00:00.031
    Kernel stack not resident.
    

    The limits of the user-mode stack are kept in the Teb.

    2: kd> !teb 00007ff74e94c000
    TEB at 00007ff74e94c000
        ExceptionList:        0000000000000000
        StackBase:            00000001027d0000
        StackLimit:           00000001027c2000
        SubSystemTib:         0000000000000000
        FiberData:            0000000000001e00
        ArbitraryUserPointer: 0000000000000000
        Self:                 00007ff74e94c000
        EnvironmentPointer:   0000000000000000
        ClientId:             0000000000000604 . 0000000000004228
        RpcHandle:            0000000000000000
        Tls Storage:          000000010132dfc0
        PEB Address:          00007ff74ecfa000
        LastErrorValue:       1008
        LastStatusValue:      c0000034
        Count Owned Locks:    2
        HardErrorMode:        0
    

    We now use the trick we learned some time ago where we grovel the stack of a thread without knowing where its stack pointer is.

    In this case, the groveling is made easier because we already know that everybody is waiting on the data lock. The data lock is taken in only two functions, so it's a matter ot looking for any occurrences of one of those two functions. And here it is: Data­Wrapper::Verify­Data.

    00000001`027cef88  00007ffe`f1bbac9a NTDLL!NtWaitForSingleObject+0xa
    00000001`027cef90  00000000`00006ac8
    00000001`027cef98  00007ffe`ef1bd085 KERNELBASE!WaitForSingleObjectEx+0xa5
    00000001`027cefa0  00000000`00006ac8
    00000001`027cefa8  00000000`00000000
    00000001`027cefb0  00000000`026d0000
    00000001`027cefb8  00000000`00000000
    00000001`027cefc0  00000001`01739ee0
    00000001`027cefc8  00000000`00000001
    00000001`027cefd0  00000000`00000048
    00000001`027cefd8  00000001`00000001
    00000001`027cefe0  00000000`00000000
    00000001`027cefe8  00000000`00000000
    00000001`027ceff0  00000000`00000000
    00000001`027ceff8  00000000`00000000
    00000001`027cf000  00000000`00000000
    00000001`027cf008  00000000`00000000
    00000001`027cf010  00000000`00000000
    00000001`027cf018  00007ff7`eefe6540 FABRIKAM!Lock::IsInitialized+0xfc
    00000001`027cf020  00000000`00000000
    00000001`027cf028  00000000`3b803fa0
    00000001`027cf030  00000000`00006ac8
    00000001`027cf038  00007ff7`eeff3c43 FABRIKAM!AccessRequest::WaitTimeout+0x87
    00000001`027cf040  00000000`ffffffff
    00000001`027cf048  10f513ec`6a161fb8
    00000001`027cf050  00000000`00000000
    00000001`027cf058  00000000`00006ac8
    00000001`027cf060  00000000`3b803fb8
    00000001`027cf068  00007ff7`eeff3cc0 FABRIKAM!AccessRequest::Wait+0x18
    00000001`027cf070  00000000`8007000e
    00000001`027cf078  00000000`fd416ff0
    00000001`027cf080  4fe80c4a`51236583
    00000001`027cf088  00000000`3b803fb8
    00000001`027cf090  00000000`ffffffff
    00000001`027cf098  00000000`00000000
    00000001`027cf0a0  00000000`3b803fa0
    00000001`027cf0a8  00007ff7`da8375b7 FABRIKAM!DataAccess::RequestAccess+0x93
    00000001`027cf0b0  00000000`3b803fa0
    00000001`027cf0b8  00000000`3b803fb8
    00000001`027cf0c0  4fe80c4a`51236583
    00000001`027cf0c8  10f513ec`6a161fb8
    00000001`027cf0d0  00000000`00000076
    00000001`027cf0d8  00007ff7`f07a8619 CONTOSO!Widget::SetColor+0x9
    00000001`027cf0e0  000095dc`90897985
    00000001`027cf0e8  00000000`00000110
    00000001`027cf0f0  00000000`00000000
    00000001`027cf0f8  00000000`00000000
    00000001`027cf100  00007ff7`4dd8000c
    00000001`027cf108  00007ff7`f07a85e5 CONTOSO!Widget::UpdateColor+0x39
    00000001`027cf110  00000000`ffc03f90
    00000001`027cf118  00000001`027cf1e8
    00000001`027cf120  00000000`ffc03fc8
    00000001`027cf128  00000000`00000000
    00000001`027cf130  00000000`00000000
    00000001`027cf138  00007ff7`4dd8000c
    00000001`027cf140  00007ff7`da5bc420 FABRIKAM!DataAccess::`vftable'+0x18
    00000001`027cf148  00000000`ffc03f90
    00000001`027cf150  00000001`027cf260
    00000001`027cf158  00007ff7`f0b3459c CONTOSO!DataWrapper::VerifyData+0x428
    00000001`027cf160  00000000`3b803fa0
    00000001`027cf168  00007ff7`f0ed1d30 CONTOSO!g_DataManager
    00000001`027cf170  00000000`fe196f10
    00000001`027cf188  00000000`00000001
    00000001`027cf190  00000000`00000000
    00000001`027cf198  00000001`027cf270
    00000001`027cf1a0  00000001`027cf480
    00000001`027cf1a8  00007ff7`00000001
    00000001`027cf1b0  00000001`027cf220 
    00000001`027cf1b8  00000000`00000000 
    00000001`027cf1c0  00000000`00000000 
    

    I left the red herrings in place just to make things a little more interesting.

    The Data­Wrapper::Verify­Data method enters the critical section and then calls Data­Access::Request­Access via a virtual method call:

    00007ff7`f0b3458f mov     dword ptr [rsp+28h],eax
    00007ff7`f0b34593 mov     eax,dword ptr [rsi+10h]
    00007ff7`f0b34596 mov     dword ptr [rsp+20h],eax
    00007ff7`f0b3459a call    qword ptr [rdi] ←
    

    Let's disassemble the start of Data­Access::Request­Access to see how it sets up its stack. This will help us interpret the other values in the stack dump.

    0: kd> u 00007ff7`da8375b7-93
    FABRIKAM!DataAccess::RequestAccess
    ;; function prologue
    00007ff7`da837524 mov     rax,rsp
    00007ff7`da837527 mov     qword ptr [rax+8],rbx
    00007ff7`da83752b mov     qword ptr [rax+20h],r9
    00007ff7`da83752f mov     qword ptr [rax+18h],r8
    00007ff7`da837533 mov     qword ptr [rax+10h],rdx
    00007ff7`da837537 push    rbp
    00007ff7`da837538 push    rsi
    00007ff7`da837539 push    rdi
    00007ff7`da83753a push    r12
    00007ff7`da83753c push    r13
    00007ff7`da83753e push    r14
    00007ff7`da837540 push    r15
    00007ff7`da837542 sub     rsp,70h
    ;; function body
    00007ff7`da837546 lea     rsi,[rcx+18h]
    00007ff7`da83754a mov     rdi,rcx
    00007ff7`da83754d mov     rbp,r9
    00007ff7`da837550 mov     rcx,rsi
    00007ff7`da837553 call    qword ptr [FABRIKAM!_imp_EnterCriticalSection]
    00007ff7`da837559 xor     r13b,r13b
    00007ff7`da83755c mov     ebx,8007000Eh
    ...
    00007ffe`da8375b1 call    FABRIKAM!AccessRequest::Wait
    

    We can replay the above code in our head and annotate the stack trace accordingly. On entry to the function, the stack pointer is 00000001`027cf158 (the return address). The function stashes some registers in the caller-provided spill area and it pushes others onto the stack, and then it subtracts some space for local variables as well as for outbound parameters of functions it intends to call.

    /-00000001`027cf0b0  00000000`3b803fa0
    | 00000001`027cf0b8  00000000`3b803fb8
    | 00000001`027cf0c0  4fe80c4a`51236583
    | 00000001`027cf0c8  10f513ec`6a161fb8
    | 00000001`027cf0d0  00000000`00000076
    | 00000001`027cf0d8  00007ff7`f07a8619 CONTOSO!Widget::SetColor+0x9
    | 00000001`027cf0e0  000095dc`90897985
    | 00000001`027cf0e8  00000000`00000110
    | 00000001`027cf0f0  00000000`00000000
    | 00000001`027cf0f8  00000000`00000000
    | 00000001`027cf100  00007ff7`4dd8000c
    | 00000001`027cf108  00007ff7`f07a85e5 CONTOSO!Widget::UpdateColor+0x39
    | 00000001`027cf110  00000000`ffc03f90
    \-00000001`027cf118  00000001`027cf1e8
      00000001`027cf120  00000000`ffc03fc8 // VerifyData's r15
      00000001`027cf128  00000000`00000000 // VerifyData's r14
      00000001`027cf130  00000000`00000000 // VerifyData's r13
      00000001`027cf138  00007ff7`4dd8000c // VerifyData's r12
      00000001`027cf140  00007ff7`da5bc420 FABRIKAM!DataAccess::`vftable'+0x18 // VerifyData's rdi
      00000001`027cf148  00000000`ffc03f90 // VerifyData's rsi
      00000001`027cf150  00000001`027cf260 // VerifyData's rbp
      00000001`027cf158  00007ff7`f0b3459c CONTOSO!DataWrapper::VerifyData+0x428 ← ESP is here
      00000001`027cf160  00000000`3b803fa0 // VerifyData's rbx
      00000001`027cf168  00007ff7`f0ed1d30 CONTOSO!g_DataManager // VerifyData's rdx
      00000001`027cf170  00000000`fe196f10 // VerifyData's r8
      00000001`027cf188  00000000`00000001 // VerifyData's r9
      00000001`027cf190  00000000`00000000
      00000001`027cf198  00000001`027cf270
      00000001`027cf1a0  00000001`027cf480
      00000001`027cf1a8  00007ff7`00000001
      00000001`027cf1b0  00000001`027cf220 
      00000001`027cf1b8  00000000`00000000 
      00000001`027cf1c0  00000000`00000000 
    

    The region marked in brackets is the 0x70 bytes of space for local variables and outbound parameters. Notice that some red herring function pointers are in that space. Those are probably variables that haven't been initialized yet, and the memory happened previously to have been used to hold some return addresses.

    A reassuring observation is that the rdx coming from Verify­Data is the address of CONTOSO!g_Data­Manager. That is the second function parameter (or first, if you aren't counting the hidden this) to Request­Access.

    Another reassuring observation is that that Verify­Data's rdi points into the vtable for Data­Access, since that matches the code we saw at the call point: call qword ptr [rdi].

    The mov rdi, rcx instruction in the function body tells us that the function stashed its this pointer in rdi. That's good info to keep track of, because that will let us look at the Data­Access object once we figure out what is in rdi.

    The next function on the stack is Access­Request::Wait.

    FABRIKAM!AccessRequest::Wait:
    00007ff7`eeff3ca8 sub     rsp,38h
    00007ffe`eeff3cb3 mov     dword ptr [rsp+20h],0FFFFFFFFh
    00007ffe`eeff3cbb call    FABRIKAM!AccessRequest::WaitTimeout
    00007ffe`eeff3cc0 add     rsp,38h
    00007ffe`eeff3cc4 ret
    

    This function doesn't bother saving any registers; it just reserves space for local variables and outbound parameters. From inspection, you can see that this is a simple wrapper that passes all its parameters onward to Wait­Timeout, with an INFINITE tacked onto the end, so this function has no local variables at all. Everything is just for outbound parameters.

    We can annotate some more entries in our stack trace.

    00000001`027cf070  00000000`8007000e // spill space for WaitTimeout
    00000001`027cf078  00000000`fd416ff0 // spill space for WaitTimeout
    00000001`027cf080  4fe80c4a`51236583 // spill space for WaitTimeout
    00000001`027cf088  00000000`3b803fb8 // spill space for WaitTimeout
    00000001`027cf090  00000000`ffffffff // INFINITE parameter
    00000001`027cf098  00000000`00000000 // unused
    00000001`027cf0a0  00000000`3b803fa0 // unused
    00000001`027cf0a8  00007ff7`da8375b7 FABRIKAM!DataAccess::RequestAccess+0x93
    

    The next function on the list is Access­Request::Wait­Timeout.

    FABRIKAM!AccessRequest::WaitTimeout:
    00007ff7`eeff3bbc mov     qword ptr [rsp+8],rbx
    00007ff7`eeff3bc1 mov     qword ptr [rsp+10h],rbp
    00007ff7`eeff3bc6 push    rsi
    00007ff7`eeff3bc7 sub     rsp,20h
    00007ff7`eeff3bcb mov     ebx,edx
    00007ff7`eeff3bcd mov     rsi,rcx
    00007ff7`eeff3bd0 mov     edx,0Bh
    00007ff7`eeff3bd5 mov     rcx,r8
    

    This function stashes two registers in the parameter spill space, pushes one onto the stack, and reserves another 0x20 bytes for local use (outbound parameters).

    00000001`027cf040  00000000`ffffffff // spill space for WaitForSingleObjectEx
    00000001`027cf048  10f513ec`6a161fb8 // spill space for WaitForSingleObjectEx
    00000001`027cf050  00000000`00000000 // spill space for WaitForSingleObjectEx
    00000001`027cf058  00000000`00006ac8 // spill space for WaitForSingleObjectEx
    00000001`027cf060  00000000`3b803fb8 // Wait's rsi
    00000001`027cf068  00007ff7`eeff3cc0 FABRIKAM!AccessRequest::Wait+0x18
    00000001`027cf070  00000000`8007000e // Wait's rbx
    00000001`027cf078  00000000`fd416ff0
    00000001`027cf080  4fe80c4a`51236583
    00000001`027cf088  00000000`3b803fb8
    

    Notice that the stashed rbx value is 8007000E, which conveniently lines up with the mov ebx,8007000Eh instruction in Data­Access::Request­Access. That's a bit reassuring, since it's another sign that we're on the right track.

    Next up is Wait­For­Single­Object­Ex.

    0: kd> u 00007ffe`ef1bd085 -a5
    KERNELBASE!WaitForSingleObjectEx
    00007ffe`ef1bcfe0 mov     r11,rsp
    00007ffe`ef1bcfe3 mov     qword ptr [r11+8],rbx
    00007ffe`ef1bcfe7 mov     dword ptr [r11+18h],r8d
    00007ffe`ef1bcfeb push    rsi
    00007ffe`ef1bcfec push    rdi
    00007ffe`ef1bcfed push    r14
    00007ffe`ef1bcfef sub     rsp,80h
    00007ffe`ef1bcff6 mov     ebx,r8d
    

    Incorporating this prologue into our stack annotation yields

    /-00000001`027cefa0  00000000`00006ac8 // spill space for NtWaitForSingleObject
    | 00000001`027cefa8  00000000`00000000 // spill space for NtWaitForSingleObject
    | 00000001`027cefb0  00000000`026d0000 // spill space for NtWaitForSingleObject
    | 00000001`027cefb8  00000000`00000000 // spill space for NtWaitForSingleObject
    | 00000001`027cefc0  00000001`01739ee0
    | 00000001`027cefc8  00000000`00000001
    | 00000001`027cefd0  00000000`00000048
    | 00000001`027cefd8  00000001`00000001
    | 00000001`027cefe0  00000000`00000000
    | 00000001`027cefe8  00000000`00000000
    | 00000001`027ceff0  00000000`00000000
    | 00000001`027ceff8  00000000`00000000
    | 00000001`027cf000  00000000`00000000
    | 00000001`027cf008  00000000`00000000
    | 00000001`027cf010  00000000`00000000
    \-00000001`027cf018  00007ff7`eefe6540 FABRIKAM!Lock::IsInitialized+0xfc
      00000001`027cf020  00000000`00000000 // WaitTimeout's r14
      00000001`027cf028  00000000`3b803fa0 // WaitTimeout's rdi
      00000001`027cf030  00000000`00006ac8 // WaitTimeout's rsi
      00000001`027cf038  00007ff7`eeff3c43 FABRIKAM!AccessRequest::WaitTimeout+0x87
      00000001`027cf040  00000000`ffffffff // WaitTimeout's rbx
      00000001`027cf048  10f513ec`6a161fb8
      00000001`027cf050  00000000`00000000 // WaitTimeout's r8
      00000001`027cf058  00000000`00006ac8
    

    Ooh, another red herring function pointer got caught in the local variables.

    Putting everything together results in the following annotated stack, with red herrings removed.

    00000001`027cef88  00007ffe`f1bbac9a NTDLL!NtWaitForSingleObject+0xa
    00000001`027cef90  00000000`00006ac8
    00000001`027cef98  00007ffe`ef1bd085 KERNELBASE!WaitForSingleObjectEx+0xa5
    00000001`027cefa0  00000000`00006ac8
    00000001`027cefa8  00000000`00000000
    00000001`027cefb0  00000000`026d0000
    00000001`027cefb8  00000000`00000000
    00000001`027cefc0  00000001`01739ee0
    00000001`027cefc8  00000000`00000001
    00000001`027cefd0  00000000`00000048
    00000001`027cefd8  00000001`00000001
    00000001`027cefe0  00000000`00000000
    00000001`027cefe8  00000000`00000000
    00000001`027ceff0  00000000`00000000
    00000001`027ceff8  00000000`00000000
    00000001`027cf000  00000000`00000000
    00000001`027cf008  00000000`00000000
    00000001`027cf010  00000000`00000000
    00000001`027cf018  00007ff7`eefe6540
    00000001`027cf020  00000000`00000000 // WaitTimeout's r14
    00000001`027cf028  00000000`3b803fa0 // WaitTimeout's rdi
    00000001`027cf030  00000000`00006ac8 // WaitTimeout's rsi
    00000001`027cf038  00007ff7`eeff3c43 FABRIKAM!AccessRequest::WaitTimeout+0x87
    00000001`027cf040  00000000`ffffffff // WaitTimeout's rbx
    00000001`027cf048  10f513ec`6a161fb8
    00000001`027cf050  00000000`00000000 // WaitTimeout's r8
    00000001`027cf058  00000000`00006ac8
    00000001`027cf060  00000000`3b803fb8 // Wait's rsi
    00000001`027cf068  00007ff7`eeff3cc0 FABRIKAM!AccessRequest::Wait+0x18
    00000001`027cf070  00000000`8007000e // Wait's rbx
    00000001`027cf078  00000000`fd416ff0
    00000001`027cf080  4fe80c4a`51236583
    00000001`027cf088  00000000`3b803fb8
    00000001`027cf090  00000000`ffffffff // INFINITE parameter
    00000001`027cf098  00000000`00000000
    00000001`027cf0a0  00000000`3b803fa0
    00000001`027cf0a8  00007ff7`da8375b7 FABRIKAM!DataAccess::RequestAccess+0x93
    00000001`027cf0b0  00000000`3b803fa0
    00000001`027cf0b8  00000000`3b803fb8
    00000001`027cf0c0  4fe80c4a`51236583
    00000001`027cf0c8  10f513ec`6a161fb8
    00000001`027cf0d0  00000000`00000076
    00000001`027cf0d8  00007ff7`f07a8619
    00000001`027cf0e0  000095dc`90897985
    00000001`027cf0e8  00000000`00000110
    00000001`027cf0f0  00000000`00000000
    00000001`027cf0f8  00000000`00000000
    00000001`027cf100  00007ff7`4dd8000c
    00000001`027cf108  00007ff7`f07a85e5
    00000001`027cf110  00000000`ffc03f90
    00000001`027cf118  00000001`027cf1e8
    00000001`027cf120  00000000`ffc03fc8 // VerifyData's r15
    00000001`027cf128  00000000`00000000 // VerifyData's r14
    00000001`027cf130  00000000`00000000 // VerifyData's r13
    00000001`027cf138  00007ff7`4dd8000c // VerifyData's r12
    00000001`027cf140  00007ff7`da5bc420 FABRIKAM!DataAccess::`vftable'+0x18 // VerifyData's rdi
    00000001`027cf148  00000000`ffc03f90 // VerifyData's rsi
    00000001`027cf150  00000001`027cf260 // VerifyData's rbp
    00000001`027cf158  00007ff7`f0b3459c CONTOSO!DataWrapper::VerifyData+0x428
    00000001`027cf160  00000000`3b803fa0 // VerifyData's rbx
    00000001`027cf168  00007ff7`f0ed1d30 CONTOSO!g_DataManager // VerifyData's rdx
    00000001`027cf170  00000000`fe196f10 // VerifyData's r8
    00000001`027cf188  00000000`00000001 // VerifyData's r9
    00000001`027cf190  00000000`00000000
    00000001`027cf198  00000001`027cf270
    00000001`027cf1a0  00000001`027cf480
    00000001`027cf1a8  00007ff7`00000001
    00000001`027cf1b0  00000001`027cf220 
    00000001`027cf1b8  00000000`00000000 
    00000001`027cf1c0  00000000`00000000 
    

    From this, we can also suck out the this pointer passed to Data­Access::Request­Access. We saw that it was stashed in rdi. The Wait function doesn't use rdi (because if it did, it would have saved the old value), so its rdi is the same as Request­Access's rdi. Similarly, the Wait­Timeout function does not use rdi. Therefore, when Wait­For­Single­Object saves the rdi register, it is saving the value from Data­Access::Request­Access.

    00000001`027cf028  00000000`3b803fa0 // WaitTimeout DataAccess's rdi
    

    And that is the this pointer that lets us study the Data­Access object to figure out why its access request is not completing.

Page 1 of 436 (4,360 items) 12345»