• The Old New Thing

    The increasing urgency of a request to fill out a survey


    Here are some email messages I received from an automated system.

    October 20: "This survey must be completed no later than November 14."

    October 31: "Reminder, this survey must be completed no later than November 14."

    November 12: "Final reminder, this survey must be completed no later than November 14."

    November 14: "Extension, this survey must be completed no later than November 19."

    November 19: "Final reminder, this survey must be completed no later than November 19."

    November 28: "This survey must be completed no later than December 16."

    I gave in and clicked the survey link.

    The link was broken.

    I guess the people running the survey said, "Gosh, we're getting no responses. Let's extend the deadline."


  • The Old New Thing

    Clarifying the documentation on wildcards accepted by FindFirstFile/FindFirstFileEx


    A customer asked for clarification in the documentation for Find­First­File and related functions, such as Find­First­File­Ex and Find­First­File­Transacted

    Does Find­First­File­Ex support multiple wildcards in the lpFile­Name parameter? The documentation is not clear. We are hoping to pass something like C:\Directory1\Directory2\*abc*\def*.txt. Note that there are two asterisks in the directory portion as well as an asterisk in the file name portion. Should we expect this to work? The documentation is not very explicit about this scenario. It just says

    lpFileName: The directory or path, and the file name, which can include wildcard characters, for example, an asterisk (*) or a question mark (?).

    I agree that the documentation is ambiguous here. One interpretation of the sentence is

    The directory or path, and the file name, any of which can include wildcard characters, for example, an asterisk (*) or a question mark (?).

    Or it could be interpreted as

    The directory or path, and the file name. The file name can include wildcard characters, for example, an asterisk (*) or a question mark (?).

    You can have multiple wildcards, but all wildcards must exist in the file name portion. The search pattern lets you apply a filter to a search within a single directory. It is not a SQL query.

    I submitted a documentation change request to clarify the sentence to the second version above:

    The directory or path, and the file name. The file name can include wildcard characters, for example, an asterisk (*) or a question mark (?).
  • The Old New Thing

    We got around three

    In this story about Restoration Hardware's mail order extravagance, there is a little statistic:

    Industry surveys from groups like the Direct Marketing Association estimate that catalogues get average response rates of four to five per cent.

    That reminded me a story back from the days when Microsoft sold Fortran PowerStation, the development suite for the scientific programming language. A developer from the Windows 95 team decided to try a change of pace and switched into sales and marketing, and he wound up working on Fortran PowerStation.

    One of his attempts to drum up interest was to include a reply card in a periodic mailing to subscribers of some general computer programming magazine. This was back in the days when people subscribed to computer programming magazines, like, in print. Also back in the days when the publishers would send out little playing-card-deck-sized blocks of business reply cards for various products.

    As he explained it to us, "We sent out around ten thousand cards. They say that a typical response rate for a direct mailing is four to five percent. We got three."

    Okay, three percent is a bit low, but given that Fortran is not exactly an up-and-coming product, it's still quite respectable.

    "No, not three percent. Three responses."

  • The Old New Thing

    Playing with synchroninzation barriers


    A synchronization barrier is a synchronization object that works like this:

    • A synchronization barrier knows how many threads it is managing.
    • Each thread that calls Enter­Synchronization­Barrier blocks.
    • When the last thread enters the synchronization barrier, all threads are released.
    • Once a thread exits the synchronization barrier, it can re-enter it, at which point it blocks again, and the cycle repeats.

    The idea here is that you have a multi-step process, and each thread must complete a step before any thread can go on to the next step. For example, you might have a sequence of steps like this:

    • Everybody enters the room and sits down.
    • Wait for everybody to be seated.
    • Start the movie.
    • Everybody watches the movie.
    • After the movie ends, everybody leaves the room and stands outside.
    • Wait for everybody to leave the room.
    • One person locks the door.
    • Everybody says good-bye and goes home.

    The synchronization barrier takes care of the wait for everybody to... part.

    It is not uncommon that an action needs to be taken after all threads have cleared the barrier, and the action needs to be performed exactly once. In the example above, one such action is "Start the movie." To support this pattern, the Enter­Synchronization­Barrier returns TRUE to exactly one thread; all the other threads get FALSE.

    Here's a Little Program that demonstrates the synchronization barrier pattern. Each thread is person who wants to watch the movie.

    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define WATCHERS 4
    HANDLE movieFinished;
    // Watchers gonna watch.
    DWORD CALLBACK MovieWatcherThread(void* p)
      int id = PtrToInt(p);
      // Build a string that we use to prefix our messages.
      char tag[WATCHERS + 1];
      for (int i = 0; i < WATCHERS; i++) {
        tag[i] = (i == id) ? (char)('1' + i) : ' ';
      tag[WATCHERS] = 0;
      printf("%s Sitting down\n", tag);
      if (EnterSynchronizationBarrier(&barrier, 0)) {
        // We are the one who should start the movie.
        printf("%s Starting the movie\n", tag);
        // For demonstration purposes, the movie is only one second long.
        LARGE_INTEGER dueTime;
        dueTime.QuadPart = -1000000LL;
        SetWaitableTimer(movieFinished, &dueTime, 0,
                         nullptr, nullptr, FALSE);
      // Watch the movie until it ends.
      printf("%s Enjoying the movie\n", tag);
      WaitForSingleObject(movieFinished, INFINITE);
      // Now leave the room.
      printf("%s Leaving the room\n", tag);
      if (EnterSynchronizationBarrier(&barrier, 0)) {
        // We are the one who should lock the door.
        printf("%s Locking the door\n", tag);
      printf("%s Saying good-bye and going home\n", tag);
      return 0;
    int __cdecl main(int, char**)
      movieFinished = CreateWaitableTimer(nullptr, TRUE, nullptr);
      InitializeSynchronizationBarrier(&barrier, WATCHERS, -1);
      HANDLE threads[WATCHERS];
      for (int i = 0; i < WATCHERS; i++) {
        DWORD threadId;
        threads[i] = CreateThread(nullptr, 0, MovieWatcherThread,
                                  IntToPtr(i), 0, &threadId);
      // Wait for the demonstration to complete.
      WaitForMultipleObjects(WATCHERS, threads, TRUE, INFINITE);
      return 0;

    Each thread represents a person who wants to watch the movie. We sit down, and then wait for everybody else to sit down by entering the synchronization barrier. The call to Enter­Synchronization­Barrier returns when everybody has sat down.

    The watcher whose call to Enter­Synchronization­Barrier returned TRUE takes responsibility for starting the movie.

    And then everybody watches the movie, waiting until the movie ends.

    Once that's done, each person leaves the room, and then enters another synchronization barrier to wait until everybody is outside.

    The synchronization barrier will nominate one person to lock the door.

    Everybody says good-bye and goes home.

    There is one tricky thing about synchronization barriers: Knowing when it is safe to reenter a synchronization barrier after exiting it. If the synchronization barrier is always used by the same pool of threads (like we did here), then there's no problem. The thread clearly has exited the synchronization barrier if it manages to run code and try to enter it again.

    The tricky part is if the collection of threads participating in the barrier changes over time. The rule is that when a thread exits the barrier, then it releases a "unit of synchronization" that another thread can consume by entering the barrier. Usually, the unit is consumed by the same thread that exits the barrier, but that thread could instead hand the unit to another thread and let that other thread take over the job. It is important that there be a causal link between the exiting thread and the future entering thread, so that the future entering thread knows that the previous exiting thread has fully exited.

    Suppose there are three steps: Step 1 is handled by threads A and B, and Steps 2 and 3 are handled by threads B and C. You don't want to do this:

    Thread A Thread B Thread C
    Work on step 1 Work on step 1 Idle
    Enter­Synchronization­Barrier Enter­Synchronization­Barrier
    Signal thread C to start
    Work on step 2 Work on step 2
    Enter­Synchronization­Barrier Enter­Synchronization­Barrier
    Work on step 3 Work on step 3

    The problem here is that thread C may call Enter­Synchronization­Barrier before thread A has exited it. There is no causal link between thread A exiting the barrier and thread C entering it, which creates the race condition.

    In this case, you can solve the problem by having thread A be the one to signal thread C to start working on step 2. That way the "unit of synchronization" is positively handed from thread A to thread C.

  • The Old New Thing

    Investigating a problem constructing a security descriptor to deny thread-specific access rights to Everyone


    A customer had a question about how to set up a security access mask.

    How can I deny thread-specific access rights to Everyone?

    Specifically, I want to deny the Everyone group the following rights when I create a process:


    How do I create the access mask for this? Will this function work?

    DWORD GetDeniedMask()
      DWORD accessMask = 0;
      GENERIC_MAPPING genmap;
      genmap.GenericRead = WRITE_DAC | WRITE_OWNER;
      genmap.GenericWrite = WRITE_DAC | WRITE_OWNER;
      genmap.GenericExecute = WRITE_DAC | WRITE_OWNER;
      genmap.GenericAll = WRITE_DAC | WRITE_OWNER |
      MapGenericMask(&accessMask, &genmap);
      return accessMask;

    This question is so confused it's hard to say where to start.

    Why are you trying to deny these accesses to Everyone? Note that Everyone includes the owner of the process, which means that the owner of the process can't even terminate his own process! Furthermore, many normal operations need accesses like the ones you are denying. You are going to end up with a process that can't do much, not even to itself. For example, the Virtual­Alloc function needs PROCESS_VM_OPERATION access.¹ A process that can't allocate any memory is not going to get very far. Some of these accesses are needed by the process creator in order to do things like set up the initial environment and command line. And anti-malware software is going to block the creation of any process that refuses to let the anti-malware software inspect it!

    The customer explained,

    A security audit uncovered that our processes granted the rights listed above to Everyone, so we are seeing what we can do to deny those rights to Everyone while still allowing those rights to the creator and people in the right security group.

    Is it a security risk to grant the above listed rights to Everyone? If so, how do we deny them to Everyone while still allowing it to the right people? We assume we need to pass custom security attributes as the lpProcess­Attributes and lpThread­Attributes parameters when we call the Create­Process function, but we need help building those security attributes.

    Since deny actions override allow actions,² you can't deny something to Everyone, and then grant it to a special subgroup. The deny on Everyone will override the allow on the subgroup.

    The way to do this is not to deny Everyone, but rather to stop allowing Everyone. A security principal receives access if there is an applicable allow rule and no applicable deny rule.² So remove the spurious allow rule.

    Actually, where is the allow rule for Everyone coming from? The default process security does not grant Everyone those accesses. The customer must be doing something unusual.

    Here is the code we are using to set the security attributes on the process. [I have converted the long C++ code into equivalent pseudo-C# code for readability. -Raymond]

    var acl = new AccessControlList();
    // Deny some accesses to AU and WD.
    var deniedMask = GetDeniedMask();
    acl.AddDenyAce(AuthenticatedUsersSid, deniedMask);
    acl.AddDenyAce(WorldSid, deniedMask);
    // Grant some accesses to AU and WD.
    var worldMask = GetAllowedMask();
    acl.AddAllowAce(AuthenticatedUsersSid, worldMask);
    acl.AddAllowAce(WorldSid, worldMask);

    It's not clear why they denied and granted identical accesses both to Authenticated Users and to Everyone. (aka World). Since Authenticated Users are a subset of Everyone, all the rules for Authenticated Users are redundant.

    We need to peel away yet another layer of the onion. What is the custom access mask being granted to Everyone?

    Here is the Get­Allowed­Mask function.

    DWORD GetAllowedMask()
     GENERIC_MAPPING genmap;
     genmap.GenericRead = GENERIC_READ |
                          FILE_GENERIC_READ |
     genmap.GenericWrite = 0;
     genmap.GenericExecute = GENERIC_EXECUTE |
                             FILE_GENERIC_EXECUTE |
     genmap.GenericAll = GENERIC_READ |
                         GENERIC_EXECUTE ;
     MapGenericMask(&accessMask, &genmap);
     return accessMask;

    Here we see the same confusion that started the whole thing.

    The customer appears not to understand what the Map­Generic­Mask function does, or what it is for.

    I will pause now so you can review my earlier discussion of the Map­Generic­Mask function, what it does, and its intended usage pattern.

    Welcome back. If you read and understood that article, you'll observe that this customer completely misses the point of the Map­Generic­Mask function. They are using it to calculate information on the client side. But if you're on the client side, you don't need to convert GENERIC_READ to a specific mask. That's the server's job! Just ask for generic access and go home.

    Anyway, let's see what happens. The Get­World­Access­Mask function is passing a hard-coded access mask and a hard-coded generic mapping. We can walk through the code ourselves to see what happens.

    • Since GENERIC_READ is set, we remove it and replace it with genmap.Generic­Read, which is GENERIC_READ | FILE_GENERIC_READ | SECTION_MAP_READ, resulting in GENERIC_READ | FILE_GENERIC_READ | SECTION_MAP_READ | GENERIC_EXECUTE.

    Hey, wait a second. We're making things worse! The whole point of Map­Generic­Mask is to get rid of generic mappings, but this genmap structure says, "To get rid of GENERIC_READ, convert it to this other stuff that includes GENERIC_READ."

    This is like reading some tips on how to rid your room of outdated clutter, and one of them says, "If you see an old magazine, you can get rid of it by putting a fern next to it." Um, that's not actually getting rid of anything. You just added more stuff.

    • Since GENERIC_WRITE is not set, nothing is done with Generic­Write.
    • Since GENERIC_ALL is not set, nothing is done with Generic­All.
    • Finally, the Map­Generic­Mask function removes all generic access bits, because it promises never to return any generic access bits.

    The result of all these shenanigans is that we are granting Everyone the follow access mask:

    GENERIC_READ 0x80000000

    0x00120089 =
    0x00020000 |
    0x00000001 |
    0x00000080 |
    0x00000008 |
    SECTION_MAP_READ 0x00000004
    GENERIC_EXECUTE 0x20000000
    0x001200A0 =
    0x00020000 |
    0x00000080 |
    0x00000020 |
    SECTION_MAP_EXECUTE 0x00000008
    Grand total 0x001200AD

    Actually, this mask makes no sense. It is combining file-specific access masks and section-specific access masks. And then applying them to a process and a thread! (Background reading.)

    This is like looking at the menu for a Chinese restaurant and deciding that you want the Number 21 (cashew chicken), then looking at the menu for an Indian restaurant and deciding that you want the Number 18 (saag paneer), then calling a Greek restaurant, ordering the number 21 and 18 for take-out, then calling a Thai restaurant, and ordering the number 21 and 18 for take-out. And then you wonder why the Greek restaurant gave you a moussaka and a pork souvlaki, and the Thai restaurant gave you a phad thai and a yam nua. Where's your cashew chicken and saag paneer?

    Let's see what Greek food we ended up ordering by accident.

    0x00020000 |
    0x00000001 |
    0x00000080 |
    0x00000008 |
    READ_DAC |

    0x00020000 |
    0x00000080 |
    0x00000020 |
    READ_DAC |

    Well, that explains why the process grants so many weird accesses to Everyone: Because you're granting all these weird accesses to Everyone!

    I think we can predict what Thai food we ordered by accident, but let's do the math.

    0x00020000 |
    0x00000001 |
    0x00000080 |
    0x00000008 |
    READ_DAC |

    0x00000004 ???? undefined ????
    0x00020000 |
    0x00000080 |
    0x00000020 |
    READ_DAC |
    0x00000008 THREAD_GET_CONTEXT

    From reading the confused code, it appears that the customer wants to grant read and execute rights to Everyone, but it's not clear why. In particular, Execute rights don't have intrinsic meaning for most types of objects, aside from files (to see if you can execute them) and memory (to see if you can execute code from them). Consequently, many object types overload Execute to mean something else. For example, our Gizmo object overloads Execute to mean start/stop.

    If it's the case that the customer merely wants to grant permission to execute the program to Everyone, then that's done by applying the ACL to the executable file itself.

    Assuming the presumption above is true, then the solution to the customer's problem is simple: Delete all the code that tries to create a custom security descriptor and just pass NULL as the security descriptor for the process and thread. This creates the process with default security, which is just fine for what you want.

    The customer wrote back,

    Thanks. This is code we acquired recently, and the code base is so old that nobody knows exactly what this custom security attribute is trying to do.

    ¹ But you luck out because Get­Current­Process returns a handle with full access, so the ACLs on the process object don't get a chance to flex their muscles if the process is talking about itself.

    ² Reality is more complex than this simple statement, but the details are not important to the story. The statements are true enough.

  • The Old New Thing

    Stupid JavaScript debugging tricks: Abusing the conditional breakpoint


    Your favorite JavaScript debugger may very well have a conditional breakpoint facility, and if it does, you can abuse it to do things unrelated to conditional breakpoints.

    Since conditional breakpoints are based on evaluating an expression, you can use an expression with side effects, like, say, logging.

    Breakpoint Condition

    When the breakpoint location is reached, the expression is evaluated and the breakpoint is hit only if the expression is true or has changed.

    console.log("click received")
    ⵙ Is true
    ⵔ Has changed

    OK Cancel

    The console.log function returns undefined, so the condition is never true, but that's okay, because we're executing the expression for its side effect of printing a string to the console.

    Here, we just logged a hard-coded string, but you can log any expression you want. For example, you could use

    console.log("current selection is " + this.currentSelection)

    Or even more fun:


    where you've defined

    function getStack() { try { throw new Error(); } catch (e) { return e.stack; } }

    What, you didn't define that function? No problem. Just evaluate it in your immediate window and boom, now it's defined.

    Now, printing an entire stack trace may be excessive. So store it in your object for future inspection. For example, you might do this in a constructor so that you have a stack trace of how your object got created. That way, when you have a transaction that failed to complete, you can look at the cached stack trace to see how the transaction got started.

    (this._stack = getStack()) === 42

    The extra === 42 at the end is to ensure that the value of the expression is falsy.

    Anyway, just a few quick tips for JavaScript debugging. I didn't come up with these ideas, but I'm sharing them.

  • The Old New Thing

    If You Are The One: The crazy Chinese dating show


    My wife watches 非誠勿擾, a Chinese dating game show. The title literally translates as "If not sincere, then do not bother", but that doesn't really convey what the title means. It follows in the Chinese tradition of the cryptic four-character phrase. The less cryptic version would be "If you are not sincere, then don't waste my time." The English equivalent would be something like "Serious inquiries only."

    Here are the rules of the show as I understand them:

    • The bachelor contestant could be handsome, or he could be a nerd.
    • The bachelor faces a panel of 24 women, most of whom are conventionally attractive Chinese women, although the producers toss in a few wildcards like a foreign student or a someone who is a little chubby or homely.
    • The bachelor secretly reveals to the host (and the home audience) the woman he has preselected based only on her appearance, known as the "heartbeat girl".
    • Each woman starts out with her podium light lit green.
    • If a woman decides that she is not interested in dating the bachelor, she turns off her light (which actually just turns it red).
    • If all the women turn off their lights, then the game is over, and the bachelor goes home.
    • In the first video, the bachelor introduces himself.
    • The host invites the bachelor to do a brief talent demonstration, be it dancing, singing, playing a musical instrument, whatever.
    • In the second video, the bachelor's romantic history is dramatized.
    • In the third video, the bachelor's friends and relatives describe him.
    • Throughout, the host and two additional consultants banter with the bachelor and the women on the panel. This is where most of the bizarre statements come out.
    • A woman who is particularly attracted to the bachelor can activate the "burst light", thereby guaranteeing a position in the final round. (I've never seen it activated more than once. I don't know if that's a rule, or that's just how it works out.)
    • After all the videos and banter are complete, if there are more than two women with their lights still green, the bachelor eliminates all but two by turning off their lights.
    • All of the women with their lights still turned on, plus the one who activated the burst light (if any), plus the heartbeat girl advance to the final round.
    • The bachelor selects a question from a menu, and prerecorded video responses from each finalist are played.
    • The bachelor asks a question of his own choosing, and each finalist gives her answer.
    • The bachelor selects the woman he wants to date. In the special case where the bachelor selects the heartbeat girl, but she had turned off her light earlier in the round, then she has the opportunity to reject the bachelor.
    • The women who did not win a date return for the next round, or the next show if this was the final round of the day.

    Anyway, my wife enjoys watching the show. Me less so, seeing as I don't understand Mandarin beyond being able to count to one hundred and maybe a dozen other vocabulary words.

    And then I learned that the show has been subtitled in English by Australian broadcaster SBS Two, and the show has taken the country by storm. Unfortunately for me, I'm not in Australia, so in order to understand the show, I have to ask my wife to translate what's going on. (Which she does, if I ask.)

    If you don't understand Mandarin and you aren't married to a translator, you can follow the Commemorative Fashion Shoes Tumblr¹ which is devoted to screencaps of the more bizarre things said on the show. The Tumblr if you are the gif does the same thing, but does not appear to be active any more.

    Bonus chatter: One should be careful not to assume that what happens on television dating shows is how dating actually works in the country of origin. After all, you wouldn't want people to base their knowledge of dating in the United States on what happens on The Bachelor or Studs. (Yes, that's Bill Nye.)

    Bonus bonus chatter: An undercover story: If you are the one. A woman describes the experience of being on the show. Covers, among other things, the careful arrangement of the 24 women on stage.

    ¹ Apparently, the winning couple win a cruise and fashion shoes, hence the strange name for the Tumblr.

  • The Old New Thing

    Why is the StartRunNOHOMEPATH policy so very specific about what it does?


    The Start­Run­No­HOME­PATH policy affects whether the user's HOME path is the current directory when you run a program from the Run dialog. But that's all it does. DWalker asks, "I wonder why the behavior is different between Start and Start.Run."

    The Start.Run dialog is not related to the Start menu. It's just some dialog box that happens to be directly invokable from Start. This dialog is also used by Task Manager when you say File.New Task, and you can also invoke it programmatically.

    The search box on the Start menu is a search box, not a run box. Its job is to use what you typed as the basis for a search and present you a list of matches, and to execute the top match if you press Enter. That it runs Notepad if you type N-O-T-E-P-A-D is a consequence of the search rather than any attempt to run the command line "notepad".

    As for why the policy is so narrowly-defined, a lot of that falls back to the customer who requested the policy in the first place. Many policies are the result of direct requests from IT departments who want to control some very precise aspect of the system because it affects their network or otherwise creates a burden on their computing infrastructure. You can usually spot these types of policies because of their extremely precise formulation.

    morlanweb wonders if there are equivalent policies for other launch points. Given that the Start­Run­No­HOME­PATH policy was created in response to a specific customer request, it stands to reason that corresponding policies for other launch points would exist only if a customer requested them. And so far, no corporate IT department has asked for them.

    As a rule, IT departments do not notify us when they no longer need a policy, so these random strange-looking policies can linger for a long time without any real clients. Not that it would help, because once the policy is published, any other company's IT department could start using it without telling us.

    These special one-off policies are tested primarily by the customers who requsted them. If the policy implementation solves their problem, then that's that. This helps to shift the support burden of the policy from the product team to the individual who requested the policy. If a service pack or a future version of Windows causes the policy to behave strangely, we rely partly on the customer letting us know. In a sense, the loser become responsible for testing his check box.

  • The Old New Thing

    The ritual of choosing your next office


    The joke at Microsoft is "Don't like your office? Don't worry. You'll be moved to a different one soon." It's not actually that bad, at least not in my experience, but the joke still stands.

    When a team moves to a new building, there is the question of who gets which office. And this is one of the few things at Microsoft that is done purely by seniority: The best offices go to people who have been at Microsoft the longest.¹

    Different teams manage things differently. The most free-for-all method is simply to give everybody a time slot, and at the appointed time, you come in, look at the available offices, and pick yours. Subject to constraints like, "All the people who work on the X component should be in this area of the building."

    More commonly, the manager of the team that is moving sits down and decides where everybody will get moved to, taking seniority into account, so that more senior people tend to get better offices.

    I heard of one manager who augmented the standard pattern: After everybody was given their office assignments, she said, "Okay, anybody can swap offices by mutual agreement. You can make side deals if you want. If you want my office, make me an offer."

    I don't know whether anybody tried to swap for her office, or what they offered to sweeten the deal.

    ¹ Of course, senior executives will pull rank and claim the best offices for themselves, using excuses like "I need an office large enough to have a meeting table," or "My doctor says that I have to have a nice view of Mount Rainier."

  • The Old New Thing

    How can I get notified when the cursor changes?


    Today's Little Program tracks changs to the cursor. You might want to do this as part of instrumentation, in order to see how often the user is staring at an hourglass, for example. (It's easier to make the Move cursor appear on demand, so I'll use that instead.)

    The magic words here are OBJID_CURSOR and Get­Cursor­Info.

    #include <windows.h>
    #include <stdio.h>
    void log()
      CURSORINFO ci = { sizeof(ci) };
      printf("showing = %d, suppressed = %d, pos = (%d, %d), handle = %p\n",
        !!(ci.flags & CURSOR_SHOWING),
        !!(ci.flags & CURSOR_SUPPRESSED),

    The log function prints information about the current cursor. For now, we just dump it to the screen, but obviously you could do something fancier with it. The CURSOR_SHOWING flag tells you whether the cursor show count is nonnegative, which is what classically controls whether the cursor is visible on the screen. The CURSOR_SUPPRESSED flag tells tells you that nominally visible cursor is not visible to the user because the user touched the screen with a finger or pen.

    void CALLBACK WinEventProc(
      DWORD event,
      HWND hwnd,
      LONG idObject,
      LONG idChild,
      DWORD idEventThread,
      DWORD time)
      if (hwnd == nullptr &&
          idObject == OBJID_CURSOR &&
          idChild == CHILDID_SELF) {
        switch (event) {
        case EVENT_OBJECT_HIDE:
          printf("cursor hidden\n");
        case EVENT_OBJECT_SHOW:
          printf("cursor shown\n");
          printf("cursor changed\n");

    Our event hook procedure checks if we're being notified about the cursor. If so, then we print some information about the event we received, and then log the cursor details.

    int __cdecl main(int, char**)
      printf("Move cursor = %p\n", LoadCursor(nullptr, IDC_SIZEALL));
      HWINEVENTHOOK hook = SetWinEventHook(
      MessageBox(nullptr, TEXT("Press Ok when bored"),
                 TEXT("Title"), MB_OK);
      return 0;

    Our main program prints the handle of the Move cursor, just to demonstrate that the handle will match the output. Next, it installs the event hook on its own process and thread. (If you want to monitor the entire process, then pass 0 for the thread ID. If you wanted to monitor all processes on the desktop, then pass 0 for both the process ID and thread ID.) Next, we display a message box to give you a way to exit the program, and to fire up a message pump. After you are bored, we remove the hook and exit.

    Now, I chose the Move cursor because it is pretty much the only cursor you can get to from a message box: Press Alt+Space, then hit M for Move. Bingo, a Move cursor. And you can see the program spit out the new cursor handle, and it should match the value printed at the start of the program.

Page 1 of 464 (4,637 items) 12345»