• The Old New Thing

    Forcing a file handle closed when it has been opened remotely

    • 23 Comments

    Today's Little Program closes a file handle that was opened remotely. It builds on previous discussion on how to use the Net­Xxx functions.

    int __cdecl wmain(int argc, wchar_t **argv)
    {
     FILE_INFO_3 *pinfo3;
     NET_API_STATUS status;
     DWORD_PTR resumeHandle = 0;
     do {
      DWORD actual, estimatedTotal;
      status = NetFileEnum(NULL, NULL, NULL, 3,
                           (LPBYTE*)&pinfo3,
                           MAX_PREFERRED_LENGTH,
                           &actual,
                           &estimatedTotal,
                           &resumeHandle);
      if (status == NERR_Success ||
          status == ERROR_MORE_DATA) {
       for (DWORD i = 0; i < actual; i++) {
        if (lstrcmpiW(argv[1], pinfo3[i].fi3_pathname) == 0) {
         wprintf(L"Closing %ls result %d\n", pinfo3[i].fi3_pathname,
                 NetFileClose(NULL, pinfo3[i].fi3_id));
         status = ERROR_NO_MORE_FILES;
         break;
        }
       }
       NetApiBufferFree(pinfo3);
      }
     } while (status == ERROR_MORE_DATA);
     return 0;
    }
    

    Forcing a network file handle closed does not actually close the handle. This makes it very different from the various "force handle closed" utilities out there. Rather, forcing a network file handle closed is accomplished by simulating a network failure, so that when the remote machine tries to use the handle again, it's told, "Wha? I'm sorry, we must have a bad connection, because I'm not sure what you're talking about." Since programs which access network resources must deal with the possibility of network connectivity loss, this deception does not violate the interface contract.

    (Doing this to handles to local resources is a much riskier undertaking, because applications expect access to local files to remain valid for the lifetime of the handle. There is no equivalent of transient network connectivity failure for local files on non-removable drives. There is also no API for simulating it.)

  • The Old New Thing

    Microspeak: Science project

    • 31 Comments

    A science project is a feature that is really cool and challenging from a technological standpoint but is way overkill for the end-user scenario at hand.

    Back in the late 1990's, a bunch of us cooked up this idea for a networked screen saver that ran at night after most people had gone home from work. You told it the physical location and compass orientation of everybody's monitor. The networked screen saver created a virtual red bouncing ball that traveled around the building in three dimensions. The screen saver showed a viewport into the three-dimensional virtual world that contained the bouncing ball.

    This is a clear example of a science project: Nobody's going to sit there and type the positions and orientations of every computer monitor in the building. Even if we wrote this screen saver, nobody would actually use it. Most of the enjoyment is in actually writing the screen saver than in actually running it.

    One type of science project has high set-up costs for low benefit, like our bouncing ball screen saver.

    Another type of science project requires hardware that very few people have right now. For example, "If you have a tablet connected to at least two touch-enabled external monitors, then you can..."

    A third type of science project is simply trying to solve a problem that nobody really considers to be a problem. You're doing it just for the Gee Whiz factor. For example, "If you have a pair of Bluetooth headphones, and you walk back into range of your computer, the computer can automatically unpause your music." Yeah, I guess you could do that, but it also means that while you are away from your computer, you're walking around looking like an idiot because you're wearing headphones.

    Now, there may be an actual useful feature hiding inside a science project, but until you find that feature and bring it to the surface, what you basically have is a science project.

  • The Old New Thing

    Redistributing computers among offices for heating purposes

    • 25 Comments
    Some time ago, I joked about the people who rearrange computers in their house during the winter in order to use them as space heaters.

    Turns out this happens a lot at Microsoft. One of my friends said that one of his coworkers used a small heater in her office to keep warm. On the other hand, his office always ran warm because of all the computers in it. They hit upon a simple solution to both problems: "Now she's using a 12 core/24 thread space heater that's a lot quieter than her old one."

    At one point in time, I had a large number of computers in my office, including an Itanium prototype. (You knew it was a prototype because it contained Engineering Styrofoam.) The thing generated a lot of heat. My friend across the hall, on the other hand, had a cold office. Solution: With some help from colleagues, we moved the Itanium across the hall. Two problems solved.

  • The Old New Thing

    Up and down often substitute for compass directions, but you have to know when you've taken it too far

    • 26 Comments

    The official curriculum for seventh grade students in the state of Washington includes Washington history and geography. My friend the seventh grade teacher typically includes as part of this curriculum an assignment wherein each student is assigned one of the state's counties on which to produce a brief report.

    It is common to substitute up and down for north and south when speaking informally, but it is also important to know when you've taken the substitution too far. One student's report on Pierce County began with the following sentence:

    Pierce County is at the bottom of Puget Sound.
  • The Old New Thing

    There is no longer any pleasure in reading the annual Microsoft injury reports

    • 22 Comments

    Microsoft is required by law to file reports on employees who have sustained injuries on the job. They are also required to post the reports in a location where employees can see them. These reports come out every year on February 1.

    Back in the old days, these reports were filled out by hand, and reading them was oddly amusing for the details. My favorite from the mid 1990's was a report on an employee who was injured on the job, and the description was simply pencil lead embedded in hand.

    Sadly, the reports are now computerized, and there isn't a place to describe the nature of each injury. It's just a bunch of numbers.

    Numbers are nice, but they don't tell a story in quite the same way.

  • The Old New Thing

    When the option becomes so second-nature you forget that it's an option

    • 17 Comments

    A user of the imaginary Program Q program wanted to write an automated test that created a table, then ran various sub-test which communicated among each other by updating that table.

    When my test tries to create a table, the program asks the following question:

    q install server -r testdb
    
    Setting up this machine to be a registered table server...
    
    Registered table servers must adhere to Microsoft information 
    security policies. See http://programq/policy for details.
    If you have questions, contact mailto:qpolicy.
    
    Do you agree to adhere to Microsoft policies regarding
    registered table servers (y/n/q)?
    

    Is there a way to suppress the question? I can't pre-create a single server that all the tests connect to, because multiple tests running simultaneously would end up colliding with each other. I would prefer that each test run on its own isolated table server, but when I try to install a table server on the machine being tested, I get the above prompt.

    Why not just create an unregistered table server instead? Just leave off the -r flag. Give your problem description, there appears to be no need for the table server to be registered.
    Ah, didn't know about the ability to create an unregistered server. Works great!

    The user was apparently so accustomed to creating registered table servers that he didn't realize that there was any other kind. My guess is that he had no idea what the -r flag did; he just cargo-culted it from somewhere.

    Remember: The target audience for Program Q is not non-technical end-users. The target audience is other programmers, and this person was clearly a programmer since he was writing an automated test!

  • The Old New Thing

    Fabio coming to Redmond. Also: Whey Protein

    • 6 Comments

    Mark your calendars: Fabio Lanzoni, better known to the world as simply Fabio, will be at the Redmond Whole Foods Market on June 21 to promote his whey protein product. (Now made with real Fabio!) And unlike Martha, he will let you take a picture of him.

    By the way, ladies, he's available!

  • The Old New Thing

    Why does the access violation error message put the operation in quotation marks? Is is some sort of euphemism?

    • 24 Comments

    When an application crashes with an access violation, the error message says something like

    The instruction at "XX" referenced memory at "YY". The memory could not be "read".

    Why is the operation in quotation marks? Is this some sort of euphemism?

    The odd phrasing is a consequence of globalization. The operation name is a verb in the infinitive ("read", "write"), but depending on how the containing message is localized, it may need to take a different form. Since the kernel doesn't understand grammar, it just puts the words in quotation marks to avoid having to learn every language on the planet. Imagine if it tried:

    The memory could not be readed.

    The kernel tried to form the passive, which is normally done in English by adding "–ed" to the end of the verb. Too bad "read" and "write" are irregular verbs!

    The more conventional solution for this type of problem is to create a separate error message for each variant so that the text can be translated independently. rather than building sentences at runtime,

    The access violation error message is in a pickle, though, because the underlying status code is STATUS_ACCESS_VIOLATION, and that message contains three insertions, one for the instruction address, one for the address being accessed, and one for the operation. If there were three different status codes, like STATUS_ACCESS_VIOLATION_READ, STATUS_ACCESS_VIOLATION_WRITE, and STATUS_ACCESS_VIOLATION_EXECUTE, then a separate string could be created for each. But that's not how the status codes folks decided to do things, and the translation team was stuck having to use the ugly quotation marks.

  • The Old New Thing

    The evolution of menu templates: Introduction

    • 2 Comments

    As with dialog templates, menu templates have also gone through a four-stage evolutionary process. People don't often generate menu templates in code, although the LoadMenuIndirect function is there waiting for you once you get the urge. As a result, there aren't many questions from people trying to generate menu templates dynamically, but I'm going to go into the history of menu templates anyway, just out of a sense of completeness.

    If you're having problems with your dynamically-generated menu templates, you can ask the resource compiler to tell you what you're doing wrong by createing a *.rc file for it and compiling it into a scratch program. Dump the resource as bytes, and there's your answer. Same trick we used for dialog templates.

  • The Old New Thing

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

    • 12 Comments

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

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

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

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

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

    q select -owner bob
    

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

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

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

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

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

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

    public void MakeUnavailable(string productId)
    {
        foreach (var product in
                 from p in Inventory
                 where p.ProductId == productId
                 select p) {
            product.Available = false;
        }
    }
    
Page 378 of 439 (4,382 items) «376377378379380»