• The Old New Thing

    Raymond misreads flyers, episode 2: It Takes You

    • 17 Comments

    As part of a new phase in Microsoft's continuing recycling efforts, the recycling program got a new motto. The new motto was not announced with any fanfare. Rather, any recycling-related announcement had the new motto at the top as part of the letterhead.

    The new motto: It Takes You.

    I had trouble parsing this at first. To me, it sounded like the punch line to a Yakov Smirnoff joke.

    Episode 1.

  • The Old New Thing

    Why aren't environment variables being expanded in my RGS file?

    • 12 Comments
    A customer was having trouble with their RGS file.

    I want to include the below line in a .rgs file:

    val HandlerPath = s '%windir%\system32\awesome.dll'.
    

    When I do this, registering of the dll fails with 80002009. Any help? If I change it to

    val HandlerPath = s 'C:\windows\system32\awesome.dll'.
    

    then the registration succeeds (but of course now contains a hard-coded path).

    A common problem people have when asking a question is assuming that the person reading your question has knowledge that is a strict superset of what you know. As a result, people omit details like the answer to the question "How did you register your RGS file?"

    If all else fails read the documentation (which happens to be the #1 hit for "rgs file", or at least was at the time of this writing). And the documentation explains how the % works. And it's not for environment variable expansion.

    Just because you stick something between percent signs doesn't mean that the magical percent sign fairies are going to swoop in and perform environment variable expansion. Wishful thinking does not cause features to spring into existence.

    As the documentation says, you need to use the _ATL_REGMAP_ENTRY macro to create the mapping for replacement variables.

    This type of question reflects a certain mentality which is cute when kids do it, but frustrating when demonstrated by programmers, namely, that features exist merely because you expect them to, rather than because there's any documentation that suggests that they exist.

  • The Old New Thing

    What is the strange garbage-looking string in the "command" value of a static verb?

    • 14 Comments

    A customer from a major software vendor asked, "What is the significance of the command value that can be found under HKCR\⟨progid⟩\shell\open\command. It appears to be a copy of the default value, but with the program name replaced with apparent garbage. We've seen this both with Microsoft products as well as products by other companies. There is no mention of this value in the documentation on static verbs."

    Name Type Data
    (Default) REG_SZ "C:\Program Files\Contoso\CONTOSO.exe" /NOLOGO "%1"
    command REG_MULTI_SZ 34GY`{XL?{Y)2S($,PP>c=@0l{Ja0N8KUwy@4JdO /NOLOGO "%1"

    The customer didn't explain why they were interested in this particular registry value. Maybe they thought it was enabling some super magical powers, and they wanted to get in on that action. (If that was the case, then they failed to notice that the same command value also existed in the verb registration for their own program!)

    That strange garbage-looking string was placed there by Windows Installer (also known as MSI). It is the so-called Darwin descriptor that Windows Installer uses to figure out what program to run when the verb is invoked by the shell. For compatibility with programs that read the registry directly (because everybody knows that reading the registry is much cooler than using the API), the default value is set to something approximating the local executable's path. That default value might be incorrect if the application has moved in the meantime, and it might be missing entirely if the application is marked as install-on-demand and has never been used, but at least it keeps those rogue programs working 99% of the time.

  • The Old New Thing

    Enumerating set partitions with Bell numbers and Stirling numbers of the second kind

    • 15 Comments

    Just for fun, today's Little Program is going to generate set partitions. (Why? Because back in 2005, somebody asked about it on an informal mailing list, suggesting it would be an interesting puzzle, and now I finally got around to solving it.)

    The person who asked the question said,

    Below we show the partitions of [4]. The periods separate the individual sets so that, for example, 1.23.4 is the partition {{1},{2,3},{4}}.

    1 blocks: 1234
    2 blocks: 123.4,  124.3,  134.2,  1.234,  12.34,  13.24,  14.23
    3 blocks: 1.2.34,  1.24.3,  1.23.4,  14.2.3,  13.2.4,  12.3.4
    4 blocks: 1.2.3.4

    I replied with a hint, saying, "This page explains what you need to do, once you reinterpret the Stirling recurrence as an enumeration." Only now, writing up this post, did I realize that I linked to the same page they were quoting from.

    The key is in the sentence, "They satisfy the following recurrence relation, which forms the basis of recursive algorithms for generating them." Let's reinterpret the Stirling recurrence combinatorially. No wait, we don't need to. Wikipedia did it for us. Go read that first. If you didn't follow Wikipedia's explanation, here's another angle:

    Suppose you have a set of n elements, and you want to generate all the partitions into k blocks. Well, let's look at element n. What happens when you delete it from the partition?

    One possibility is that n was in a block all by itself. After you remove it, you are left with a partition of n − 1 elements into k − 1 blocks. Therefore, to generate the partitions where n is in a block all by itself, you generate all the partitions of n − 1 elements into k − 1 blocks, and then tack on a single block consisting of just element n.

    On the other hand, if element n was not in a block by itself, then removing it leaves a partition of n − 1 elements into k blocks. (Removing n did not decrease the number of blocks because there are still other numbers keeping that block alive.) Now, element n could have belonged to any of the k blocks, so you have k possible places where you could reinsert element n.

    Therefore, our algorithm goes like this:

    • Handle base cases.
    • Otherwise,
      • Recursively call S(n − 1, k) and add element n separately into each of the k blocks.
      • Recursively call S(n − 1, k − 1) and add a single block consisting of just n.

    Now that we spelled out what we're going to do, actually doing it is a bit anticlimactic.

    function Stirling(n, k, f) {
     if (n == 0 && k == 0) { f([]); return; }
     if (n == 0 || k == 0) { return; }
     Stirling(n-1, k-1, function(s) {
      f(s.concat([[n]])); // append [n] to the array
     });
     Stirling(n-1, k, function(s) {
      for (var i = 0; i < k; i++) {
       f(s.map(function(t, j) { // append n to the i'th subarray
        return t.concat(i == j ? n : []);
       }));
      }
     });
    }
    

    You can test out this function by logging the results to the console.

    function logToConsole(s) {
      console.log(JSON.stringify(s));
    }
    
    Stirling(4, 2, logToConsole);
    

    Let's walk through the function. The first test catches the vacuous base case where you say, "Please show me all the ways of breaking up nothing into zero blocks." The answer is, "There is exactly one way of doing this, which is to break it into zero blocks." Math is cute that way.

    The second test catches the other base cases where you say, "Please show me all the ways of breaking up n elements into zero blocks" (can't be done if n > 0, because you will always have elements left over), or you say, "Please show me all the ways of breaking up 0 elements into k blocks" (which also can't be done if k > 0 because there are no elements to put in the first block).

    After disposing of the base cases, we get to the meat of the recurrence. First, we ask for all the ways of breaking n - 1 elements into k - 1 blocks, and for each of them, we add a single block consisting of just n.

    Next, we ask for all the ways of breaking n - 1 elements into k blocks, and for each of them, we go into a loop adding n to each block. The goofy map creates a deep copy of the array and adds n to the ith block.

    Here's a walkthrough of the goofy map:

    • The concat method creates a new array consisting of the starting array with the concat parameters added at the end. If a parameter is an array, then its elements are added; otherwise the parameter itself is added. For example, [1].concat(2, [3, 4]) returns [1, 2, 3, 4]. The concat method creates a new array, and a common idiom is s.concat() to make a shallow copy of an array.
    • The map method calls the provided callback once for each element of the array s. The return values from all the callbacks are collected to form a new array, which is returned. For example, [1,2,3].map(function (v) { return v * 2; }) returns the new array [2, 4, 6].
    • The map callback is called with the subarray as the first parameter and the index as the second parameter. (There is also a third parameter, which we don't use.)
    • Therefore, if all we want to do was create a deep copy of s, we could write s.map(function (t, j) { return t.concat(); }).
    • But we don't want a perfect deep copy. We want to change the ith element. Therefore, we check the index, and if it is equal to the i, then we append n. Otherwise, we append [] which appends nothing.
    • After building the array (with modifications), we pass it to the callback function f.

    This pattern is common enough that maybe we should pull it into a helper function.

    function copyArrayOfArrays(array, indexToEdit, editor) {
     return array.map(function(e, i) {
      return i === indexToEdit ? editor(e) : e.concat();
     });
    }
    
    function Stirling(n, k, f) {
     if (n == 0 && k == 0) { f([]); return; }
     if (n == 0 || k == 0) { return; }
     Stirling(n-1, k-1, function(s) {
      f(s.concat([[n]])); // append [n] to the array
     });
     Stirling(n-1, k, function(s) {
      for (var i = 0; i < k; i++) {
       f(copyArrayOfArrays(s, i, function(e) { return e.concat(n); }));
      }
     });
    }
    

    The copy­Array­Of­Arrays function abstracts the goofy map: You give it an array of arrays, and optionally an index to edit and the function that does the editing. It copies the array of arrays and calls your editor on the element you want to edit.

    To reduce the number of memory allocations, the recursion could also be done destructively. You're then counting on the callback not modifying the result, since you're going to use it again.

    function Stirling(n, k, f) {
     if (n == 0 && k == 0) { f([]); return; }
     if (n == 0 || k == 0) { return; }
     Stirling(n-1, k, function(s) {
      for (var i = 0; i < k; i++) {
       s[i].push(n);
       f(s);
       s[i].pop();
      }
     });
     Stirling(n-1, k-1, function(s) {
      s.push([n]);
      f(s);
      s.pop();
     });
    }
    

    The original question was about enumerating all partitions (Bell numbers), and that's easy to put together from the Stirling numbers.

    function Bell(n, f) {
     for (var i = 1; i <= n; i++) {
      Stirling(n, i, f);
     }
    }
    
  • The Old New Thing

    Hey, you look Chinese, we have a class for people like you

    • 30 Comments

    (The title is a callback to this article from a few months ago.)

    A member of my extended family grew up near the city of Nanaimo, Canada. While it's true that she's ethnically Chinese, it's also true that she's a fourth generation Canadian. The community is overwhelmingly English-speaking, and English is her first language. She grew up going to an English-language school, she watched English-language television, spoke English with her friends and family, and probably dreamed dreams in English.

    Yet when the family moved to Vancouver when she was a child (I don't know the exact age, so let's say eight years old), the school district automatically enrolled her in English as a Second Language, presumably based on her Chinese last name and the fact that Mandarin and Cantonese are the mother tongues in 30% of Vancouver homes.

    Since she was only eight years old, she didn't know that the school had tagged her as a non-native English speaker. She cheerfully went to her special class for an hour each day, enjoying her time with a new teacher and new classmates.

    It wasn't for a long time (I don't know exactly, so let's say six months) that she realized, "Wait a second, this is the class for people who don't speak English well!" Once the mistake was recognized, it wasn't long before she was transferred back to the regular class.

    Though I'm kind of surprised the school district (and the class teacher) never figured out that her English was native, or, failing that, that her English was plenty good enough that she didn't need the class any more.

  • The Old New Thing

    Invoking commands on items in the Recycle Bin

    • 14 Comments

    Once you've found the items you want in the Recycle Bin, you may want to perform some operation on them. This brings us back to our old friend, IContextMenu. At this point, you're just snapping two blocks together. You have one block called Retrieving properties from items in the Recycle Bin and you have another block called Invoking verbs on items.

    For the first block, let's assume you've written a function called WantToRestoreThisItem which studies the properties of a Recycle Bin item and determines whether you want to restore it. I leave this for you to implement, since I don't know what your criteria are. Maybe you want to restore files only if they were deleted from a particular directory. Maybe you want to restore files that were deleted while you were drunk. (This assumes you have some other computer program that tracks when you're drunk.)¹ Whatever. It's your function.

    For the second block, we have a helper function which should look awfully familiar.

    void InvokeVerb(IContextMenu *pcm, PCSTR pszVerb)
    {
     HMENU hmenu = CreatePopupMenu();
     if (hmenu) {
      HRESULT hr = pcm->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL);
      if(SUCCEEDED(hr)) {
       CMINVOKECOMMANDINFO info = { 0 };
       info.cbSize = sizeof(info);
       info.lpVerb = pszVerb;
       pcm->InvokeCommand(&info);
      }
      DestroyMenu(hmenu);
     }
    }
    

    And now we snap the two blocks together.

    int __cdecl _tmain(int argc, PTSTR *argv)
    {
     HRESULT hr = CoInitialize(NULL);
     if (SUCCEEDED(hr)) {
      IShellItem *psiRecycleBin;
      hr = SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT,
                                NULL, IID_PPV_ARGS(&psiRecycleBin));
      if (SUCCEEDED(hr)) {
       IEnumShellItems *pesi;
       hr = psiRecycleBin->BindToHandler(NULL, BHID_EnumItems,
                                         IID_PPV_ARGS(&pesi));
       if (hr == S_OK) {
        IShellItem *psi;
        while (pesi->Next(1, &psi, NULL) == S_OK) {
         if (WantToRestoreThisItem(psi)) {
          IContextMenu *pcm;
          hr = psi->BindToHandler(NULL, BHID_SFUIObject,
                                  IID_PPV_ARGS(&pcm));
          if (SUCCEEDED(hr)) {
           InvokeVerb(pcm, "undelete");
           pcm->Release();
          }
         }
         psi->Release();
        }
       }
       psiRecycleBin->Release();
      }
      CoUninitialize();
     }
     return 0;
    }
    

    One annoyance of the Recycle Bin is that, at least up until Windows 7, it ignores the CMIC_MASK_FLAG_NO_UI flag. It always displays a confirmation dialog if something dangerous is about to happen (like overwriting an existing file). To mitigate this problem, we can at least reduce the number of confirmations from one-per-file to just one by batching up all the objects we want to operate on into a single context menu. For this, it's easier to go back to the classical version of the program.

    int __cdecl _tmain(int argc, PTSTR *argv)
    {
     HRESULT hr = CoInitialize(NULL);
     if (SUCCEEDED(hr)) {
      IShellFolder2 *psfRecycleBin;
      hr = BindToCsidl(CSIDL_BITBUCKET, IID_PPV_ARGS(&psfRecycleBin));
      if (SUCCEEDED(hr)) {
       IEnumIDList *peidl;
       hr = psfRecycleBin->EnumObjects(NULL,
         SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl);
       if (hr == S_OK) {
        // in a real program you wouldn't hard-code a fixed limit
        PITEMID_CHILD rgpidlItems[100];
        UINT cpidlItems = 0;
        PITEMID_CHILD pidlItem;
        while (peidl->Next(1, &pidlItem, NULL) == S_OK) {
         if (WantToRestoreThisItem(psfRecycleBin, pidlItem) &&
             cpidlItems < ARRAYSIZE(rgpidlItems)) {
          rgpidlItems[cpidlItems++] = pidlItem;
         } else {
          CoTaskMemFree(pidlItem);
         }
        }
        // restore the items we collected
        if (cpidlItems) {
         IContextMenu *pcm;
         hr = psfRecycleBin->GetUIObjectOf(NULL, cpidlItems,
                         (PCUITEMID_CHILD_ARRAY)rgpidlItems,
                         IID_IContextMenu, NULL, (void**)&pcm);
         if (SUCCEEDED(hr)) {
          InvokeVerb(pcm, "undelete");
          pcm->Release();
         }
         for (UINT i = 0; i < cpidlItems; i++) {
          CoTaskMemFree(rgpidlItems[i]);
         }
        }
       }
       psfRecycleBin->Release();
      }
      CoUninitialize();
     }
     return 0;
    }
    

    In the course of the enumeration, we save the ITEMIDLISTs of all the items we want to restore, then create one giant context menu for all of them. This is the programmatic equivalent of multi-selecting the items from the Recycle Bin and then right-clicking. We then invoke the undelete verb on the entire group.

    Okay, so now suppose you want to restore the files, but instead of restoring them to their original locations, you want to restore them to a special folder. Like, say, C:\Files I deleted while I was drunk.¹ No problem. We just need a different block to snap into: The drag/drop block.

    void DropOnRestoreFolder(IDataObject *pdto)
    {
     IDropTarget *pdt;
     if (SUCCEEDED(GetUIObjectOfFile(NULL,
            L"C:\\Files I deleted while I was drunk",
            IID_PPV_ARGS(&pdt)))) {
      POINTL pt = { 0, 0 };
      DWORD dwEffect = DROPEFFECT_MOVE;
      if (SUCCEEDED(pdt->DragEnter(pdto, MK_LBUTTON,
                                   pt, &dwEffect))) {
       dwEffect &= DROPEFFECT_MOVE;
       if (dwEffect) {
        pdt->Drop(pdto, MK_LBUTTON, pt, &dwEffect);
       } else {
        pdt->DragLeave();
       }
      }
      pdt->Release();
     }
    }
    

    And now it's just a matter of snapping out the undelete block and snapping in the drag/drop block.

    int __cdecl _tmain(int argc, PTSTR *argv)
    {
     HRESULT hr = CoInitialize(NULL);
     if (SUCCEEDED(hr)) {
      IShellFolder2 *psfRecycleBin;
      hr = BindToCsidl(CSIDL_BITBUCKET, IID_PPV_ARGS(&psfRecycleBin));
      if (SUCCEEDED(hr)) {
       IEnumIDList *peidl;
       hr = psfRecycleBin->EnumObjects(NULL,
         SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl);
       if (hr == S_OK) {
        // in a real program you wouldn't hard-code a fixed limit
        PITEMID_CHILD rgpidlItems[100];
        UINT cpidlItems = 0;
        PITEMID_CHILD pidlItem;
        while (peidl->Next(1, &pidlItem, NULL) == S_OK) {
         if (WantToRestoreThisItem(psfRecycleBin, pidlItem) &&
             cpidlItems < ARRAYSIZE(rgpidlItems)) {
          rgpidlItems[cpidlItems++] = pidlItem;
         } else {
          CoTaskMemFree(pidlItem);
         }
        }
        // restore the items we collected
        if (cpidlItems) {
         IDataObject *pdto;
         hr = psfRecycleBin->GetUIObjectOf(NULL, cpidlItems,
                         (PCUITEMID_CHILD_ARRAY)rgpidlItems,
                         IID_IDataObject, NULL, (void**)&pdto);
         if (SUCCEEDED(hr)) {
          DropOnRestoreFolder(pdto);
          pdto->Release();
         }
         for (UINT i = 0; i < cpidlItems; i++) {
          CoTaskMemFree(rgpidlItems[i]);
         }
        }
       }
       psfRecycleBin->Release();
      }
      CoUninitialize();
     }
     return 0;
    }
    

    Footnotes

    ¹ If being drunk isn't your thing, then substitute some other form of impaired judgment.

  • The Old New Thing

    Principles of economics, translated

    • 3 Comments

    Yoram Bauman, the stand-up economist translates Mankiw's ten principles of economics into English.

    The proof presented in the footnote to principle 5, as well as an extended version of the translation, can be found in the Annals of Improbable Research.

  • The Old New Thing

    Obtaining the parsing name (and pidl) for a random shell object

    • 5 Comments

    The parsing name for a shell item is handy, because it lets you regenerate the item later. Actually, the pidl for the shell item is even better, because that is the official way of saving and restoring objects. It's the pidl that gets saved in a shortcut, and since shortcuts can be copied around from machine to machine, pidls must be transportable and forward compatible. (A shortcut file created on Windows XP needs to keep working on all future versions of Windows.)

    Here's a handy little tool for grabbing the parsing name and pidl for a random shell object. Start with our scratch program, and add in the Simple­Drop­Target class, with the following tweaks:

    public:
     SimpleDropTarget() : m_cRef(1) { /* g_ppr->AddRef(); */ }
     ~SimpleDropTarget() { g_ppr->Release(); }
    
    ...
     // *** IDropTarget ***
     STDMETHODIMP DragEnter(IDataObject *pdto,
        DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
     {
      *pdwEffect &= DROPEFFECT_LINK;
      return S_OK;
     }
    
     STDMETHODIMP DragOver(DWORD grfKeyState,
       POINTL ptl, DWORD *pdwEffect)
     {
      *pdwEffect &= DROPEFFECT_LINK;
      return S_OK;
     }
    ...
    };
    

    We are not a COM local server, so we won't worry about managing our process reference. And we will accept anything that has a pidl, so we say that we will accept objects via linking. (The original code accepted by copying, which would have made us reject non-copyable objects.)

    Now we can hook these up to our scratch program.

    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
      g_hwndChild = CreateWindow(
          TEXT("edit"), nullptr, ES_MULTILINE |
          WS_CHILD | WS_VISIBLE | WS_TABSTOP,
          0, 0, 0,0, hwnd, (HMENU)1, g_hinst, 0);
      SimpleDropTarget *psdt = new(std::nothrow) SimpleDropTarget();
      if (psdt) {
        RegisterDragDrop(hwnd, psdt);
        psdt->Release();
      }
      return TRUE;
    }
    
    void
    OnDestroy(HWND hwnd)
    {
      RevokeDragDrop(hwnd);
      PostQuitMessage(0);
    }
    
    ...
        // Change CoInitialize and CoUninitialize to Ole
        if (SUCCEEDED(OleInitialize(NULL))) {
    ...
            OleUninitialize();
    

    Finally, we need to say what to do when the drop occurs.

    void AppendText(LPCWSTR psz)
    {
      SendMessageW(g_hwndChild, EM_REPLACESEL, 0, (LPARAM)psz);
    }
    
    void OpenFilesFromDataObject(IDataObject *pdto)
    {
      CComPtr<IShellItemArray> spsia;
      if (SUCCEEDED(SHCreateShellItemArrayFromDataObject(
                                      pdto, IID_PPV_ARGS(&spsia)))) {
        CComPtr<IEnumShellItems> spenum;
        spsia->EnumItems(&spenum);
        if (spenum) {
          for (CComPtr<IShellItem> spsi;
               spenum->Next(1, &spsi, nullptr) == S_OK;
               spsi.Release()) {
            CComHeapPtr<wchar_t> spszName;
            if (SUCCEEDED(spsi->GetDisplayName(
                         SIGDN_DESKTOPABSOLUTEPARSING, &spszName))) {
              AppendText(spszName);
              AppendText(L"\r\n");
            }
            CComHeapPtr<ITEMIDLIST_ABSOLUTE> spidl;
            if (SUCCEEDED(CComQIPtr<IPersistIDList>(spsi)->
                                                GetIDList(&spidl))) {
              UINT cb = ILGetSize(spidl);
              BYTE *pb = reinterpret_cast<BYTE *>
                              (static_cast<PIDLIST_ABSOLUTE>(spidl));
              for (UINT i = 0; i < cb; i++) {
                WCHAR szHex[4];
                StringCchPrintf(szHex, ARRAYSIZE(szHex),
                                L"%02X ", pb[i]);
                AppendText(szHex);
              }
              AppendText(L"\r\n");
            }
          }
        }
      }
    }
    

    When the drop occurs, we convert the data object into a shell item array, enumerate the items, and print the parsing name for the item as well as a hex dump of the pidl associated with the item.

    I guess we need some header files.

    #include <shlobj.h>
    #include <strsafe.h>
    #include <atlbase.h>
    #include <atlalloc.h>
    

    Run this program and drop the Recycle Bin onto it, say.

    ::{645FF040-5081-101B-9F08-00AA002F954E}
    14 00 1F 78 40 F0 5F 64 81 50 1B 10 9F 08 00 AA 00 2F 95 4E 00 00 
    

    This tells you two things. First, that if you want to generate the Recycle Bin from a parsing name, you can use that string that starts with two colons.

    var shell = new ActiveXObject("Shell.Application");
    var recycleBin = shell.Namespace(
          "::{645FF040-5081-101B-9F08-00AA002F954E}");
    var items = recycleBin.Items();
    for (var i = 0; i < items.Count; i++) {
     WScript.StdOut.WriteLine(items.Item(i));
    }
    

    Of course, there is a predefined enumeration for the Recycle Bin, so this was a bit of a waste. You could've just written

    var recycleBin = shell.Namespace(10);
    

    But this technique generalizes to other locations in the shell namespace that do not have a special shorthand value.

    The second thing the program tells you is that if you want to generate the Recycle Bin from a pidl, you can just use that chunk of bytes. Okay, that's not quite so interesting from a scripting point of view, but if you're manipulating pidls, this can be quite handy.

    We'll use this program a little bit in a few weeks, but at this point, it's just a "Little Program" for today.

  • The Old New Thing

    2012 mid-year link clearance

    • 23 Comments

    Another round of the semi-annual link clearance.

    And, as always, the obligatory plug for my column in TechNet Magazine:

  • The Old New Thing

    Welcome to Leavenworth, Washington's faux-Bavarian village

    • 19 Comments

    The dying logging town of Leavenworth, Washington reinvented itself in the 1960's as a faux-Bavarian village. Today, over a million tourists visit annually to enjoy the scenic mountain views, soak in the fake Bavarian atmosphere, and revel in events like the Leavenworth International Accordion Celebration which starts tomorrow, or the three-weekend-long Leavenworth Oktoberfest every October. (Mind you, the Leavenworth Oktoberfest doesn't actually start until the Munich Oktoberfest is nearly over, because Oktoberfest starts in September.)

    I found during a brief visit to Leavenworth that the people there may dress like they're from Bavaria, but they don't actually speak German.

    But at least I was there undercover.

    Some years ago, a colleague of mine was on assignment in Redmond from his home country of Austria. One weekend, he decided to pay a visit to our fake Bavarian village, and when he returned, we asked him what he thought of the town.

    "Well, for a fake Bavarian village, it's not too bad. I mean, nobody would for a moment think it was the real thing, but I appreciate the effort they went to, and it was quite a pleasant little town. But the weird thing was what happened whenever I opened my mouth to speak: People recognized that I spoke with a German accent and a flash of panic crossed their faces, as if I was going to blow the cover off their little ruse and reveal it to be a fraud."

    [Update 8:00AM: Various typos fixed.]

Page 382 of 453 (4,525 items) «380381382383384»