February, 2014

  • The Old New Thing

    How can I detect that my program was run from Task Scheduler, or my custom shortcut, or a service, or whatever

    • 53 Comments

    Suppose you want your program to behave differently depending on whether it is launched from the Start menu, or by clicking the pinned icon on the taskbar, or by Scheduled Task, or from a service, or whatever. How can a program detect and distinguish these scenarios?

    The answer is you don't. And you shouldn't try.

    Instead of trying to guess how your program was executed, you should have the launcher tell you how they are executing your program. You do this by registering a different command line for each of the scenarios, and then checking for that command line in the program. (We saw a variation of this a little while ago.)

    For example, you could have your Start menu shortcut contain one command line parameter, give the taskbar pinned shortcut a different command line parameter, register yet another command line parameter with the task scheduler, and have the service launch the program with a still different command line parameter.

    They all run the same program, but the command line parameter lets the program know what context it is being run in and alter its behavior accordingly.

    It's like creating multiple email addresses that all map to the same inbox. Many email services let you take an email address and insert a plus sign followed by anything else you like before the at-sign, and it'll all get delivered to the same inbox. The thing after the plus-sign is ignored for delivery purposes, but you can use it to help organize your inbox, so you know that the message sent to bob+expos@contoso.com is related to your fantasy baseball team, whereas bob+ff@contoso.com is something about your frequent flier account.

    One thing you shouldn't do is try to guess, however. Programs that magically change their behavior based on details of the environment lead to problems that are very difficult to debug.

    Given this discussion, perhaps you can provide guidance to this customer:

    How can my DLL detect that it is running inside a service?
  • The Old New Thing

    What does the SEE_MASK_UNICODE flag in ShellExecuteEx actually do?

    • 15 Comments

    Somebody with a rude name wonders what the SEE_MASK_UNICODE flag does.

    It does nothing.

    The flag was introduced when porting the Windows 95 shell to Windows NT. It happened further back in history than I have permission to access the Windows source code history database, but I can guess how it got introduced.

    One of the things that the porting team had to do was make Unicode versions of all the ANSI functions that Windows 95 created. Sometimes this was done by creating separate A and W versions of a function. Sometimes this was done by having separate A and W versions of an interface. Sometimes by adding additional fields to the A version of a structure with a flag that says whether the ANSI or Unicode members should be used.

    My guess is that the porting team initially decided to make Shell­Execute­Ex use that third model, where the SHELL­EXECUTE­INFO structure had a SHELL­EXECUTE­INFO­EX extension with Unicode strings, and the mask specified whether the caller preferred you to use the ANSI strings or the Unicode strings.

    Presumably they decided to change course and switch to having separate SHELL­EXECUTE­INFOA and SHELL­EXECUTE­INFOW structures. But when they switched from one model to the other, they left that flag behind, probably with the intention of removing it once all existing callers had been updated to stop passing the flag, but they never managed to get around to it.

    So the flag is just sitting in the header file even though nobody pays any attention to it.

  • The Old New Thing

    Why are leading digits converted to language-specific digit shapes, but not trailing digits, and how do I suppress the conversion entirely?

    • 18 Comments

    If you have a string like 12345ABCDE67890, and you render it on an Arabic system, you might get ٠١٢٣٤ABCDE67890. The leading digits are rendered as Arabic-Indic digits, but the trailing digits are rendered as European digits. What's going on here?

    This is a feature known as contextual digit substitution. You can specify whether European digits are replaced with native equivalents by going to the Region control panel (formerly known as Regional and Language Options), clicking on the Formats tab, going to Additional settings (formerly known as Customize this format), and looking at the options under Use native digits. The three options there correspond to the three values for LOCAL_IDIGITSUBSTITUTION.

    Programmatically, you can override the user preference (if you know that you are in a special case, like an IP address) by following the instructions in MSDN.

    • Uniscribe: Script­Apply­Digit­Substitution
    • DWrite: IDWrite­Text­Analysis­Sink::Set­Number­Substitution
    • GDI: ETO_NUMERICS­LATIN or ETO_NUMERICS­LOCAL.

    As a last resort, you can stick a Unicode NODS (U+206F) at the beginning of the string to force European digits, or a Unicode NADS (U+206E) to force national digits.

    Bonus chatter: What's the point of contextual digit substitution anyway?

    Suppose you have the string "there are 3 items remaining." (Let's say that all text in lowercase is in Arabic.) You want this 3 to be rendered in Arabic-Indic digits because it is part of an Arabic sentence. On the other hand, if you have the string "that's a really nice BMW 350." you want the 350 to be in European digits since it is part of the brand name "BMW 350".

    Contextual digit substitution chooses whether to use Arabic-Indic digits or European digits by matching them to the characters that immediately precede them. (And if no character precedes them, then it uses the ambient language.)

  • The Old New Thing

    A simple email introduction: Fan mail

    • 6 Comments

    One of my former colleagues on the Windows kernel team wasn't afraid to make changes all across the system when necessary. If the engineering team decided to upgrade to a new version of the C++ compiler, my colleague was the one who gave it a test-drive on the entire Windows source code, and fixed all the warnings and errors that kick up as well as ensuring that it passed the build verification tests before updating the compiler in the official toolset. Beyond that, my colleague also ran around being a superhero, writing tools that needed to be written, fixing tools that were broken, and generally being somebody.

    Since the effect on the Windows project was so far-reaching, everybody on the team knew this person, or at least recognized the name, and as a result, my colleage ended up receiving a lot of email about all different parts of Windows, be they bug reports, requests for help using a particular component, whatever.

    And when the question was about something outside my colleague's sphere of responsibility, the message was forwarded to the correct people with a simple introduction:

    From: A
    To: XYZ-owners, Y
    Subject: Problem with XYZ

    Fan mail.

    From: Y
    To: A
    Subject: Problem with XYZ

    Blah blah blah blah

    I've used this technique a few times, but it's been a while. I should start using it again.

    Bonus chatter: At least one of you has come out and said that you post your complaints here with the expectation that the complaints will be forwarded to the appropriate team. This expectation is false. No such forwarding occurs. This Web site is not a complaint desk.

  • The Old New Thing

    Nieces sometimes extrapolate from insufficient contextual data

    • 10 Comments

    My brother-in-law enjoys greeting his nieces when they come over to visit by throwing them into the air and asking, "叫聲我?" (Who am I?)

    The nieces happily reply, "舅舅." (Uncle.)

    He then tosses them up into the air a second time and says, "大聲啲!" (Louder!)

    And the nieces happily shout, "舅舅!"

    One time, my wife was talking with her brother at a normal volume, and his niece came into the room and said to my wife, "大聲啲! 舅舅聽唔到!" (Louder! Uncle can't hear you!)

    Update: Per Frank's suggestion below, changed the niece's outburst from "舅舅冇聽到!" The incident occurred many years ago, and I cannot remember exactly what was said, so I'll go with what's funnier.

  • The Old New Thing

    Logging the contents of every message box dialog via automation

    • 9 Comments

    Today's Little Program logs the contents of every message box dialog, or anything that vaguely resembles a message box dialog. (Since there's no way for sure to know whether a dialog box is a message box or not.)

    using System.Windows.Automation;
    
    class Program
    {
     [System.STAThread]
     public static void Main(string[] args)
     {
      Automation.AddAutomationEventHandler(
       WindowPattern.WindowOpenedEvent,
       AutomationElement.RootElement,
       TreeScope.Descendants,
       (sender, e) => {
        var element = sender as AutomationElement;
        if (element.GetCurrentPropertyValue(
         AutomationElement.ClassNameProperty) as string != "#32770") {
         return;
        }
    
        var text = element.FindFirst(TreeScope.Children,
         new PropertyCondition(AutomationElement.AutomationIdProperty, "65535"));
        if (text != null) {
         System.Console.WriteLine(text.Current.Name);
        }
       });
      System.Console.ReadLine();
      Automation.RemoveAllEventHandlers();
     }
    }
    

    This is the same pattern as the program we wrote last week, but with different guts when the window opens.

    This time, we see if the class name is #32770, which UI Spy tells us is the class name for dialog boxes. (That this is the numerical value of WC_DIALOG is no coincidence.)

    If we have a dialog, then we look for a child element whose automation ID is 65535, which UI Spy tells us is the automation ID for the text inside a message box dialog. (That the traditional control ID for static controls is -1 and 65535 is the the numerical value of (WORD)-1, is no coincidence.)

    If so, then we print the text.

    If we were cleverer, we could also confirm that the only buttons are OK, Cancel, and so on. Otherwise, we can get faked out by other dialog boxes that contain static text.

  • The Old New Thing

    How can I make sure my program is launched only from my helper program and no other parent?

    • 40 Comments

    Say you have a collection of programs which work together. One of them is the "master" program that runs the show, and it has a bunch of "assistant" programs that it launches to accomplish various subtasks. These assistants are not meant to be run by themselves; they are meant to be run only by the master program. How do you design the assistant so that it can only be run by the master?

    There's nothing you can do to force the assistant to be run only by the master, since anything you do to detect the case can be faked out by an attacker. (Worst case is that they just run your program under the debugger and patch out the code that looks for the master.) So the purpose of this test is not so much to create an airtight hatchway as it is to prevent users from randomly wandering into the Program Files directory and double-clicking stuff to see what happens.

    The simplest way of doing this is to require a command-line parameter that the master passes to say, "Hey, it's me, the master. It's okay to do that thing you do." The command line parameter could be anything. assistant.exe /run say. If the command line parameter is not present, then the assistant says, "Um, please don't run this program directly. Use the master."

    You might decide to get really fancy and make the secret handshake super-complicated, but remember that there is no added security benefit here. The user can compromise assistant.exe by simply attaching a debugger to it, at which point any defensive mechanism you create can simply be disabled by a sufficiently-resourceful attacker. (And there's a class of people who will see that you put a lot of work into protecting your assistant, and that will just convince them to work harder to circumvent the protection. Because something with this much protection must certainly be very valuable!)

    There's also a benefit to keeping the secret handshake simple: It makes it a lot easier for you to debug the assistant program. Instead of having to set up the master and then get the master to do all the things it needs to generate the secret handshake for the assistant, you can just run your assistant directly with the magic flag, and boom, you're off and debugging.

    To make it even harder to run your program by accident, you can give it an extension that is not normally executable, like .MOD. That way, it cannot be double-clicked, but you can still pass it to Create­Process or (with some cajoling) Shell­Execute­Ex.

  • The Old New Thing

    What is the programmatic equivalent to unchecking the box to prevent a file from having its contents indexed?

    • 25 Comments

    A customer wanted to know how they could write a program that automatically checked and unchecked the box that appears on a file's property sheet on the General tab, clicking the Advanced button, and then checking or unchecking the item whose name keeps changing:

    • On Windows 7, it's called Allow this file to have contents indexed in addition to file properties.
    • On Windows Vista, it's called Index this file for faster searching.
    • On Windows 2000 and Windows XP, it's called For fast searching, allow Indexing Service to index this folder.

    The checkbox maps to the file attribute formally known as FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, and informally known as FANCI (pronounced like the word fancy). Checking the box clears the attribute, and unchecking the box sets the attribute.

    The customer liaison replied, "Thanks for your assistance. The customer was able to use the System.IO.File.Set­Attributes method with the values in the System.IO.File­Attributes enumeration to manipulate the indexing attribute. The customer has a follow-up question, however: ‘I need this change to be applied recursively to all files in a entire directory subtree. Is there a single series of Visual Basic commands that will accomplish this, or do I need to write a loop by hand?’"

    This question kind of ran off topic for the product team and fell more in line with Developer Support. I suggested that the follow-up question be redirected to the Visual Basic support team.

    For me, it was interesting that (1) the customer liaison was himself not aware enough to realize that the question had changed audiences, and (2) the customer was so helpless that they couldn't look for the answer themselves.

    Bonus chatter: The file system itself pays no attention to the FANCI bit. It's just a signal to any program that does file content indexing that this particular file should be skipped. Whether any particular search index program respects the flag is something you need to take up with the vendor of that search index program. (As far as I'm aware, all Microsoft search indexes should honor the flag.)

  • The Old New Thing

    When will the static control automatically delete the image loaded into it, and when is it the responsibility of the application?

    • 25 Comments

    If you create a static control with initial contents (for example, by creating a BITMAP or ICON control in a dialog template), then the static control will load the contents upon creation and destroy the contents upon destruction. So at least in the case where you don't touch the static control, things will work automatically.

    But once you touch it, things get confusing.

    If you send the STM_SET­IMAGE message to a static control, this does a few things (assuming your parameters are all valid):

    • The previous image is replaced by the new image you passed.
    • The message returns a handle to the previous image.
    • The static control turns off automatic image deletion.

    The third part is the tricky part. If you ever (successfully) send a static control the STM_SET­IMAGE message, then it says, "Okay, it's all your problem now." You are now responsible not only for destroying the new image, but you are also responsible for destroying the old image that was returned.

    In other words, the following operation is not a nop:

    HBITMAP hbmPrev = SendMessage(hwndStatic, STM_SETIMAGE,
                                  IMAGE_BITMAP, (LPARAM)hbmNew);
    SendMessage(hwndStatic, STM_SETIMAGE,
                IMAGE_BITMAP, (LPARAM)hbmPrev);
    

    This sounds like a nop, since all you did was change the image, and then change it back. But the side effect is also that you made the static control go into your problem mode, and the original image will no longer be automatically destroyed. If you forget to destroy it yourself, then you have a leak.

    Wait, it gets worse.

    If you are using version 6 of the common controls, then things get even more confusing if you use the STM_SET­IMAGE message to change the IMAGE_BITMAP of a SS_BITMAP static control, and the bitmap you pass is a 32-bpp bitmap, and the image has a nonzero alpha channel, then the static control will make a copy of the bitmap you passed in and act as if you had passed that copy instead.¹ This by itself is no big deal, because the responsibility for destroying the image you passed in still resides with you, the application, so the rules haven't changed there.

    The nasty bit is that the application also must assume responsibility for destroying the secret copy. That bitmap you didn't even know existed and don't have a handle to? Yeah, you're on the hook for that one too.

    How unfair.

    Even more confusing is that if you send STM_SET­IMAGE a second time, it will replace the bitmap and return a handle to the secret copy (which is a bitmap you've never seen before).

    This means that the following assertion can fire:

    HBITMAP hbmPrev = SendMessage(hwndStatic, STM_SETIMAGE,
                                  IMAGE_BITMAP, (LPARAM)hbmNew);
    HBITMAP hbmBack = SendMessage(hwndStatic, STM_SETIMAGE,
                                  IMAGE_BITMAP, (LPARAM)hbmPrev);
    assert(hbmNew == hbmBack); // ??
    

    You would think that the assertion is safe because all you did was change the bitmap to hbmNew, then change it back. And when you change it back, the "previous value" is the value hbmNew you set it to on the previous line.

    Except that if hbmNew satisfies the above magic criteria, then the value in hbmBack is not hbmNew but rather the handle to the secret copy.

    Which you have to remember to destroy.

    Yuck.

    The secret copy is not too secret. You can get a handle to it by sending the STM_GET­IMAGE message. Which you now need to do when you destroy the static control, just in case it's the secret copy. You need to compare the current image against the one that you thought you passed in, and if they are different, then you have the secret copy that needs to be destroyed as an extra step.

    Yes, this sucks. I apologize.

    (My recommendation: To detect whether a "secret copy" occurred, do a STM_GET­IMAGE after your STM_SET­IMAGE and see if the handles match.)

    ¹ The secret copy is not an exact copy. (After all, if it were an exact copy, then there would be no need to create the copy. It could just use the handle you passed in.) Instead, the secret copy is a copy of the original, followed by some additional munging so that it can be displayed on the screen while respecting the alpha channel you passed in.

  • The Old New Thing

    If you cancel an operation while it's in progress, then it's not surprising that it's only half-done

    • 65 Comments

    A customer (via their customer liaison) started by asking why they were seeing an unexpected access control entry in the security descriptor of an object.

    The ACEs on the parent grant access to Administrators, CREATOR OWNER, SYSTEM, and Users, but the ACEs on the child object (which should simply have been inherited from the parent) include an extra entry for Bob. How did Bob get access to the child object? When we view the details of the ACEs, it lists the Bob entry as Inherited from parent. But there is no Bob entry in the parent!

    I observed, "Probably because Bob is the CREATOR OWNER."

    Thanks for the explanation, but even if Bob is the CREATOR OWNER, how can we explain that the permission is inherited from the parent?

    The permission is inherited from the parent because the parent has specified the rights of the CREATOR OWNER, and Bob is the creator/owner. As part of the inheritance process, the rights of the CREATOR OWNER get assigned to Bob.

    Remember that CREATOR OWNER is not a real person. It is a placeholder that gets replaced with the actual creator/owner when the object is created. If Bob created the child object, then the permissions of CREATOR OWNER will be given to Bob on the child object.

    The CREATOR OWNER is not a live entry that dynamically updates to match the current creator/owner. It is a static entry that is assigned at the point of creation. Changes to the owner in the future have no effect because the identity has already been snapshotted. (I think a less confusing name would have been simply OBJECT CREATOR, since creation happens only once.)

    (Note that there is a little extra weirdness here: If the creator is a member of the Administrators group, then the CREATOR OWNER rights get assigned to the entire Administrators group instead of the specific user who created it. You can change this behavior by tweaking the Default owner for objects created by members of the Administrators group policy.)

    The customer liaison conferred with the customer, and determined that, at least in one of the cases they were studying, Bob was not the original creator.

    What actually happened was that at some point, Bob was granted access to the parent object and all its sub-objects. Later, somebody went back to the parent object and told it to revoke Bob's access to the parent object and all its sub-objects. But "If we cancel the process fast enough, then we get the strange behavior as originally described."

    Well, duh!

    You asked for Bob's access to the parent object and all its sub-objects to be revoked, so the tool you used started a recursive tree walk from the parent object looking for any objects that Bob has access to and removing them. But if you cancel the operation partway through, then that tool didn't get a chance to finish the job, and you obviously are left in a situation where Bob's access was only partially revoked.

    The customer liaison confirmed,

    Yes, that's what happened.

    It's nice of the customer liaison to confirm the diagnosis, but it still baffles me that they were confused by this in the first place.

    To me this is one of those So what did you expect type of situations. You start an operation, then partway through, you cancel it, and then you're surprised that the operation did not run to completion.

Page 1 of 3 (27 items) 123