• The Old New Thing

    On the linguistic productivity of the word spam

    • 13 Comments

    The word spam has spawned off its own corner of the English language.

    spamdexing inflating one's rank in a Web index
    sping Trackback spam
    splog A fake blog
    spim Spam instant messages
    spasms Spam SMS messages
    spit VoIP spam
    sporgery Spamming with a forged From line
    spambot A computer which sends spam
    blam spam comments on a blog

    I'm impressed by how productive the root word spam has become.

    Gratuitous cross-promotion: My colleague Terry Zink occasionally writes about spam on his Cyber Security Blog.

  • The Old New Thing

    If you want to use GUIDs to identify your files, then nobody's stopping you

    • 37 Comments

    Igor Levicki proposes solving the problem of file extensions by using a GUID instead of a file name to identify a file.

    You can do this already. Every file on an NTFS volume has an object identifier which is formally 16-byte buffer, but let's just call it a GUID. By default a file doesn't have an object identifier, but you can ask for one to be created with FSCTL_CREATE_OR_GET_OBJECT_ID, which will retrieve the existing object identifier associated with a file, or create one if there isn't one already. If you are a control freak, you can use FSCTL_SET_OBJECT_ID to specify the GUID you want to use as the object identifier. (The call fails if the file already has an object identifier.) And of course there is FSCTL_GET_OBJECT_ID to retrieve the object identifier, if any.

    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <ole2.h>
    #include <winioctl.h>
    
    int __cdecl _tmain(int argc, PTSTR *argv)
    {
     HANDLE h = CreateFile(argv[1], 0,
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                     FILE_SHARE_DELETE, NULL,
                     OPEN_EXISTING, 0, NULL);
     if (h != INVALID_HANDLE_VALUE) {
      FILE_OBJECTID_BUFFER buf;
      DWORD cbOut;
      if (DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID,
                     NULL, 0, &buf, sizeof(buf),
                     &cbOut, NULL)) {
        GUID guid;
        CopyMemory(&guid, &buf.ObjectId, sizeof(GUID));
        WCHAR szGuid[39];
        StringFromGUID2(guid, szGuid, 39);
        _tprintf(_T("GUID is %ws\n"), szGuid);
      }
      CloseHandle(h);
     }
     return 0;
    }
    

    This program takes a file or directory name as its sole parameter and prints the associated object identifier.

    Big deal, now we have a GUID associated with each file.

    The other half is, of course, using this GUID to open the file:

    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <ole2.h>
    
    int __cdecl _tmain(int argc, PTSTR *argv)
    {
     HANDLE hRoot = CreateFile(_T("C:\\"), 0,
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                     FILE_SHARE_DELETE, NULL,
                     OPEN_EXISTING,
                     FILE_FLAG_BACKUP_SEMANTICS, NULL);
     if (hRoot != INVALID_HANDLE_VALUE) {
      FILE_ID_DESCRIPTOR desc;
      desc.dwSize = sizeof(desc);
      desc.Type = ObjectIdType;
      if (SUCCEEDED(CLSIDFromString(argv[1], &desc.ObjectId))) {
       HANDLE h = OpenFileById(hRoot, &desc, GENERIC_READ,
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                     FILE_SHARE_DELETE, NULL, 0);
       if (h != INVALID_HANDLE_VALUE) {
        BYTE b;
        DWORD cb;
        if (ReadFile(h, &b, 1, &cb, NULL)) {
         _tprintf(_T("First byte of file is 0x%02x\n"), b);
        }
        CloseHandle(h);
       }
      }
      CloseHandle(hRoot);
     }
     return 0;
    }
    

    To open a file by its GUID, you first need to open something—anything—on the volume the file resides on. Doesn't matter what you open; the only reason for having this handle is so that OpenFileById knows which volume you're talking about. In our little test program, we use the C: drive, which means that the file search will take place on the C: drive.

    Next, you fill in the FILE_ID_DESCRIPTOR, saying that you want to open the file by its object identifier, and then it's off to the races with OpenFileById. Just as a proof of concept, we read and print the first byte of the file that was opened as a result.

    Notice that the file you open by its object identifier does not have to be in the current directory. It can be anywhere on the C: drive. As long as you have the GUID for a file, you can open it no matter where it is on the drive.

    You can run these two programs just to enjoy the thrill of opening a file by its GUID. Notice that once you get the GUID for a file, you can move it anywhere on the drive, and OpenFileById will still open it.

    (And if you want to get rid of those pesky drive letters, you can use the volume GUID instead. Now every file is identified by a pair of GUIDs: the volume GUID and the object identifier.)

    So Igor's dream world where all files are referenced by GUID already exists. Why isn't everybody switching over to this utopia of GUID-based file identification?

    You probably know the answer already: Because people prefer to name things with something mnemonic rather than a GUID. Imagine a file open dialog in this dream world. "Enter the GUID of the file you wish to open, or click Browse to see the GUIDs of all the files on this volume so you can pick from a list." How long would this dialog survive?

    For today, you don't have to call me Raymond. You can call me {7ecf65a0-4b78-5f9b-e77c-8770091c0100}, or "91c" for short.

    (And I've totally ignored the fact that using GUIDs to identify files does nothing to solve the problem of trying to figure out what program should be used to open a particular file.)

    Bonus chatter: You can also open files by their file identifer, which is a volume-specific 64-bit value. But I chose to use the GUID both for the extra challenge, and just to show that Igor's dream world already exists.

  • The Old New Thing

    DRUNK HULK's insightful commentary in all-caps (and faux-rudimentary English)

    • 7 Comments

    DRUNK HULK. Just read it.

    • 11:07 AM Feb 5th: DRUNK HULK SAW SPEED 3: LAKE HOUSE! WHAT BORE WAY TO END TRILOGY!
    • 6:58 AM Feb 8th: DRUNK HULK HOPE SMALLVILLE FINALE ANSWER ALL BIG QUESTION! LIKE HOW STUPID SHOW STAY ON AIR FOR 10 YEAR!
    • 7:33 AM Jan 19th: DRUNK HULK INTEREST IN BUY KINDLE 3! BUT WILL DRUNK HULK UNDERSTAND WHAT GO ON IF NO READ PREVIOUS ONE!
  • The Old New Thing

    Why does WaitForMultipleObjects return ERROR_INVALID_PARAMETER when all the parameters look valid to me?

    • 14 Comments

    A customer asked for assistance with the WaitForMultipleObjects function:

    I am getting ERROR_INVALID_PARAMETER when calling Wait­For­Multiple­Objects even though all the parameters are valid as far as I can tell. I've narrowed it down to this simple program.

    int main()
    {
     int i;
     HANDLE Handles[4];
    
     // Create the events
     for (i = 0; i < 4; i++) {
      Handles[i] = CreateEvent(NULL, FALSE, FALSE, TEXT("Test"));
      if (Handles[i] == NULL) {
       printf("Failed to create event - test failed\n"); return 0;
      }
     }
    
     // Set them all to signaled
     for (i = 0; i < 4; i++) SetEvent(Handles[i]);
    
     // Wait for all of them - we expect this to return WAIT_OBJECT_0
     printf("WaitForMultipleObjects returned %d\n",
            WaitForMultipleObjects(4, Handles, TRUE, INFINITE));
    
     return 0;
    }
    

    First of all, thank you for narrowing the issue down to a minimal program that illustrates the problem. You'd be surprised how often a customer says, "I'm having problem with function X. Here's a program that illustrates the problem." And then attaches a huge project that doesn't compile because it is written in some development environment different from the one you have on your machine.

    The problem here is that you are passing four handles to the same event to Wait­For­Multiple­Objects with the bWait­All parameter set to TRUE. The Wait­For­Multiple­Objects function rejects duplicates if you ask it to wait for all of the objects. Why is that?

    Well, consider this program: It creates a named auto-reset event (as is "obvious" from the FALSE second parameter passed to Create­Event) and stores a handle to it in Handles[0]. The second through fourth calls to Create­Event merely create new handles to the same auto-reset event because the name matches an existing event. The second loop sets that same event four times. And then the Wait­For­Multiple­Objects asks to wait for all of the handles to be signaled. But since all four handles refer to the same object, it's being asked to wait until the event has reached the state where the wait can complete four times simultaneously. (Huh?)

    Recall that Wait­For­Multiple­Objects does not alter the state of any of the waited objects until the wait completes. If you ask it to wait for both an event and a semaphore, and the event is signaled but the semaphore is not, then the function will leave the event signaled while it waits for the semaphore. Only when all the items being waited for are signaled will the Wait­For­Multiple­Objects function perform whatever actions are appropriate for acquiring a signaled object and return.

    Okay, so we asked it to wait on the same auto-reset event four times. But that's nonsense: An auto-reset event is just a stupid semaphore which can have at most one token. But in order for the wait to succeed, it needs four tokens. That's never going to happen, so the wait is nonsensical.

    More generally speaking, Wait­For­Multiple­Objects returns ERROR_INVALID_PARAMETER if you pass bWaitAll = TRUE and there are any duplicates in the handle array (either identical handles, or different handles to the same underlying object). It doesn't try to puzzle out the objects and say, "Well, let me see if this is a reasonable combination of objects to wait on more than once"; it just sees the duplicate and says "Forget this!"

    Going back to the customer's original problem: We asked why they were creating four handles to the same object, and what they expected when waiting for an auto-reset event to have four available tokens (which it never will), and the customer admitted that it was just an error in their code. The original version of the code used a named event and waited on it with Wait­For­Single­Object, and when they modified the code to make it support multiple events, they forgot to give each event a different name.

  • The Old New Thing

    iPhone pricing as economic experiment

    • 42 Comments

    Back in 2005, Slate's Tim Harford wondered why Microsoft didn't raise the introductory price of Xbox 360 game consoles. With the price set at $300, lines were long and shortages were many. Harford's readers came up with their own theories for resisting the laws of supply and demand and holding to a fixed price.

    The Xbox 360 is hardly unique in this respect. When there's a hot product, manufacturers hold to the original price and let the lines grow, the shortages fester, and the customers get more frustrated. Think Tickle Me Elmo or Cabbage Patch Kids. Even though from an economic-theoretical standpoint, a product that has sold out with unmet demand is a product whose price was set too low.

    With the iPhone, Apple unwittingly ran the experiment that Harford proposed. There were lines, but by some reports, the lines weren't all that bad. After the initial demand subsided, Apple did what the economists say they should have done: They lowered the price. And the people who bought the phones at the higher price complained (forcing Apple to offer a store credit) and one of them even sued. Slate's Daniel Gross opines on the lessons learned.

  • The Old New Thing

    Shortcuts are serializable objects, which means that they can be stored in places other than just a file

    • 21 Comments

    It's true that the vast majority of the time, people consider the shell shortcut object as synonymous with the .lnk file it is normally saved into, shortcuts need not spend their time in a file. You can put a shortcut anywhere you can save a hunk of bytes. Here's a program that creates a shortcut to the file name passed on the command line (make sure it's a full path), and then serializes the shortcut to a blob of bytes (in the form of a HGLOBAL). Once that's done, it reconstitutes the bytes back into a shortcut object and sucks information out of it.

    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <shlobj.h>
    #include <ole2.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <atlbase.h>
    
    HGLOBAL CreateShellLinkInMemory(PCWSTR pszFile)
    {
     BOOL fSuccess = FALSE;
     HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE, 0);
     if (hglob) {
      CComPtr<IStream> spstm;
      if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) {
       CComPtr<IShellLink> spsl;
       if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {
        if (SUCCEEDED(spsl->SetPath(pszFile))) {
         CComQIPtr<IPersistStream> spps(spsl);
         fSuccess = spps && SUCCEEDED(spps->Save(spstm, TRUE));
        }
       }
      }
     }
     if (fSuccess) return hglob;
     if (hglob) GlobalFree(hglob);
     return NULL;
    }
    

    After creating the shortcut object, we serialize it into a stream backed by a chunk of memory we record in a HGLOBAL. The shortcut object itself is no longer anywhere to be seen. It's been dehydrated into a pile of dust like in that old Star Trek episode.

    But this time, we know how to bring it back.

    IShellLink *CreateShellLinkFromMemory(HGLOBAL hglob)
    {
     IShellLink *pslReturn = NULL;
     CComPtr<IStream> spstm;
     if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) {
      CComPtr<IShellLink> spsl;
      if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {
       CComQIPtr<IPersistStream> spps(spsl);
       if (spps && SUCCEEDED(spps->Load(spstm))) {
        pslReturn = spsl.Detach();
       }
      }
     }
     return pslReturn;
    }
    

    We create a new shortcut object and tell it to restore itself from the chunk of memory we squirreled away. Bingo, the shortcut is back, ready for action.

    int __cdecl wmain(int argc, WCHAR **argv)
    {
     if (SUCCEEDED(CoInitialize(NULL))) {
      HGLOBAL hglob = CreateShellLinkInMemory(argv[1]);
      if (hglob) {
       CComPtr<IShellLink> spsl;
       spsl.Attach(CreateShellLinkFromMemory(hglob));
       if (spsl) {
        WCHAR szTarget[MAX_PATH];
        if (spsl->GetPath(szTarget, MAX_PATH, NULL, 0) == S_OK) {
         wprintf(L"Welcome back, shortcut to %s\n", szTarget);
        }
       }
       GlobalFree(hglob);
      }
      CoUninitialize();
     }
     return 0;
    }
    

    Since shortcuts can be stored anywhere, you can't rely on the file name to distinguish between shortcuts to files and shortcuts to folders because there may not be a file name at all! (What's the file name for our HGLOBAL?) Even if you decide that the convention applies only to shortcuts saved in a file, you've created an additional burden on people who manipulate shortcut files: They have to check whether the target is a file or folder before choosing the file name, and if the target of the shortcut changes, they may have to rename the file as well. This is a real problem for the standard file property sheet: If you change the shortcut target from the Shortcut page, this might change the underlying file name. If you had also made changes to the Security page, it will try to update the security attributes on the old file name, even though the Shortcut page had renamed it. Oops, none of the other property sheet pages work, because they are now operating on a file that no longer exists!

    Exercise: Under what conditions would it be useful to store a shortcut in memory rather than in a file? (Answer.)

  • The Old New Thing

    How long does an idle UNC connection remain active before it is automatically disconnected?

    • 12 Comments

    When you access a resource via a UNC, the Windows network redirector keeps the virtual circuit open for a while even after you close the resource. This is done to take advantage of locality: If you access a network resource once, you're probably going to access it again in a short time, so the redirector leaves the connection open for a little bit, on the off chance that you're going to use it again. For example, if copying a bunch of files from a server via a UNC, once one file copy is complete, the next one is going to start very shortly thereafter.

    If there is no activity on a connection for a while, then the redirector decides, "Okay, well, I guess that's all there is for that" and closes the connection.

    The default duration for this "UNC grace period" is ten minutes. You can customize it by setting the KeepConn parameter. Increasing the value will keep the connection active longer, which is a greater convenience for the client, but it also increases the load on the server.

  • The Old New Thing

    Dr. Watson and the bluescreen - a story from the trenches

    • 14 Comments

    A fellow Microsoft employee volunteered a story from his prior work at a hospital as their enterprise architect.

    I received an escalation from our Tier 1 service desk on a Dr. Watson. Why would I get a simple escalation? Strange...

    Since I hadn't seen the outside of my cubicle for a while, I decided to walk over to talk to the customer in person.

    The employee having the problem was named Dr. Watson. His computer had bluescreened.

    I still get a chuckle out of that years later. That little unexpected name collision threw twelve people in the IT service and support teams into disarray.

  • The Old New Thing

    Sharktopus: Just when you thought it was safe to see what movies are coming out

    • 27 Comments

    Sharktopus: Half-shark. Half-octopus. All-killer.

    I am not making that up.

    The Web site is sharktopusmovie.com, presumably because sharktopus.com was already taken. I am not making that up.

    I guess they wanted to ride the coattails of Megashark vs. Giant Octopus?

    Even more disturbing discovery: Megashark vs. Giant Octopus has a sequel: Megashark versus Crocosaurus.

    One of my colleagues says that he's going to wait for the sequel: Sharktopuses on a Plane.

  • The Old New Thing

    If an operation results in messages being sent, then naturally the target window must be processing messages for the operation to complete

    • 16 Comments

    If an operation includes as part of its processing sending messages, then naturally the target window for those messages must be processing messages (or more precisely, the thread which owns the target window must be processing messages) in order for the operation to complete. Why? Because processing messages is the only way a window can receive messages!

    It's sort of tautological yet not obvious to everyone.

    Generally you run into this problem when you try to manipulate a window from a thread different from the one which created the window. Since windows have thread affinity, operations from off-thread typically need to get moved onto the thread which owns the window because that's where the window really "lives".

    The window manager will often try to see how much it can do without marshalling to the thread which owns the window, but when message traffic is involved, you are pretty much stuck. Messages are delivered to a window on the thread to which the window belongs, and there's no way around that.

    There are subtle ways in which a function called off-thread can result in message traffic. Generally speaking, you should just assume that any operation on a window may generate messages: Even if they don't do so today, they may do so in the future. For example, changing a window's style did not generate message traffic in early versions of Windows, but in Windows 95, it began generating WM_STYLECHANGING and WM_STYLECHANGED messages. This isn't called out explicitly in the documentation for SetWindowLong but it's implied by the documentation for WM_STYLECHANGING and WM_STYLECHANGED.

    Why isn't there an explicit callout in the documentation for SetWindowLong? At the time the SetWindowLong documentation was originally written, the WM_STYLECHANGING and WM_STYLECHANGED messages did not exist. Therefore the documentation was complete at the time of writing. Circumstances changed elsewhere in the system that had secondary effects on SetWindowLong, but nobody bothered to update the documentation, probably because it didn't even occur to anybody that these effects existed. And then these secondary effects lead to tertiary effects: SetScrollInfo may change the window style to add or remove the WS_HSCROLL or WS_VSCROLL style, which in turn results in a call to SetWindowLong which in turn results in sending the WM_STYLECHANGING and WM_STYLECHANGED messages. Next come quaternary effects on functions like FlatSB_SetScrollInfo, since they call SetScrollInfo as part of their functioning. And so on, and so on. Just tracking down the full ripple effect of those two new messages is probably impossible.

    But the root cause of all these ripple effects is operating on a window (particularly modifying a window) from a thread different from the thread that owns the window. Avoid that, and you'll avoid the whole issue of which operations generate messages and which manage to sneak by without needing to send any messages (at least not yet).

Page 122 of 438 (4,378 items) «120121122123124»