• The Old New Thing

    When will GetSystemWindowsDirectory return something different from GetWindowsDirectory?

    • 30 Comments

    Most of the time, the Get­Window­Directory returns the Windows directory. However, as noted in the documentation for Get­System­Windows­Directory:

    With Terminal Services, the Get­System­Windows­Directory function retrieves the path of the system Windows directory, while the Get­Windows­Directory function retrieves the path of a Windows directory that is private for each user. On a single-user system, Get­System­Windows­Directory is the same as Get­Windows­Directory.

    What's going on here, and how do I test this scenario?

    When Terminal Services support was being added to Windows NT 4.0 in the mid 1990's, the Terminal Services team discovered that a lot of applications assumed that the computer was used by only one person, and that that person was a local administrator. This was the most common system configuration at the time, so a lot of applications simply assumed that it was the only system configuration.

    On the other hand, a Terminal Server machine can have a large number of users, including multiple users connected simultaneously, and if the Terminal Services team took no special action, you would have found that most applications didn't work. The situation "most applications didn't work" tends not to bode well for adoption of your technology.

    Their solution was to create a whole bunch of compatibility behaviors and disable them if the application says, "Hey, I understand that Terminal Server machines are different from your average consumer machine, and I know what I'm doing." One of those compatibility behaviors is to make the Get­Windows­Directory function return a private writable directory rather than the real Windows directory, because old applications assumed that the Windows directory was writable, and they often dumped their private configuration data there.

    The signal to disable compatibility behaviors is the IMAGE_DLLCHARACTER­ISTICS_TERMINAL_SERVER_AWARE flag in the image attributes of the primary executable. You tell the linker that you want this flag to be set by passing the /TSAWARE:YES parameter on the command line. (At some point, the Visual Studio folks made /TSAWARE:YES the default for all new projects, so you are probably getting this flag set on your files without even realizing it. You can force it off by going to Configuration Properties, and under Linker, then System, change the "Terminal Server" setting to "Not Terminal Server Aware".)

    Note that only the flag state on the primary executable has any effect. Setting the flag on a DLL has no effect. (This adds to the collection of flags that are meaningful only on the primary executable.)

    The other tricky part is that the Terminal Server compatibility behaviors kick in only on a Terminal Server machine. The way you create a Terminal Server machine has changed a lot over the years, as has the name of the feature.

    • In Windows NT 4.0, it was a special edition of Windows, known as Windows NT 4.0 Terminal Server Edition.
    • In Windows 2000, the feature changed its name from Terminal Server to Terminal Services and became an optional server component rather than a separate product. You add the component from Add/Remove Programs.
    • In Windows Server 2003 and Windows Server 2008, you go to the Configure Your Server Wizard and add the server rôle "Terminal Server."
    • In Windows Server 2008 R2, the feature changed its name again. The instructions are the same as in Windows Server 2008, but the rôle name changed to "Remote Desktop Services".
    • In Windows Server 2012, the feature retained its name but became grouped under the category "Virtual Desktop Infrastructure." This time, you have to enable the rôle server "Remote Desktop (RD) Session Host."

    Terminal Services is the Puff Daddy of Windows technologies. It changes its name every few years, and you wonder what it will come up with next.

  • The Old New Thing

    The publicity machine continues: A chat with Scott Hanselman and Hanselminutes

    • 11 Comments

    Scott Hanselman let me know he was going to be in town, and after some negotiation with the company PR department (who probably get the massive heebie-jeebies from this whole blog thing), I was able to accept his invitation to appear on his weekly podcast, HanselMinutes. We sat down for a little chat, and a few weeks later, I became Show #56.

    I haven't had the nerve to listen to it myself. I hope I came off okay.

    In other self-promotion news (did I mention yet that I wrote a book?), it looks like I will be in Palo Alto on April 21 for a family event, and I'll likely spend the 20th visiting Microsoft's Silicon Valley Campus. If you'd like me to pay a visit to your user group or whatever, let me know.

  • The Old New Thing

    It's not just on the Internet that nobody knows you don't have a real office

    • 13 Comments

    Sure, your company don't have an office, but that's okay. You can rent a fake one, with a fake receptionist, fake staff, and fake conference rooms, and whenever somebody stops by asking for you, the receptionist is trained to say that you're "out".

  • The Old New Thing

    Microspeak: Teaming

    • 12 Comments

    At training sessions, you don't participate in team-building exercises. No, that's old-fashioned terminology, the sort of thing those old stodgy Web 1.0 dinosaurs would say. The new word is teaming.

    Note: This Microspeak entry was submitted by a colleague from the UK, so it may be peculiar to the UK dialect of Microspeak.

    Pre-emptive clever comment: Verbing weirds language.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    How many days long is a one-day sale? The answer might surprise you

    • 20 Comments

    A friend of mine received a flyer for a major department store proudly proclaiming that they were having a One-Day-Only sale.

    Sale prices were in effect on Saturday and Sunday.

    Previously on the subject For large values of 1. If this keeps up, I may have to create a subcategory for it.

  • The Old New Thing

    Debugging a hang: Chasing the wait chain inside a process

    • 32 Comments

    Today we're going to debug a hang. Here are some of the (redacted) stacks of the process. I left some red herrings and other frustrations.

    0: kd> !process ffffe000045ef940 7
    PROCESS ffffe000045ef940
        SessionId: 1  Cid: 0a50    Peb: 7ff6b661f000  ParentCid: 0a0c
        DirBase: 12e5c6000  ObjectTable: ffffc0000288ae80  HandleCount: 1742.
        Image: contoso.exe
    
            THREAD ffffe000018d68c0  Cid 0a50.0a54  Teb: 00007ff6b661d000 Win32Thread: fffff90143635a90 WAIT: (WrUserRequest) UserMode Non-Alertable
                ffffe000046192c0  SynchronizationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!KeWaitForMultipleObjects+0x44e
            0xfffff960`0038bed0
            0x1
            0xffffd000`24257b80
            0xfffff901`43635a90
            0xd
            0xffffe000`00000001
            0xfffff803`ffffff00
    
            THREAD ffffe000045f88c0  Cid 0a50.0a8c  Teb: 00007ff6b64ea000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000041c1830  SynchronizationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`248ebc40)
            ntdll!ZwWaitForSingleObject+0xa
            ntdll!RtlpWaitOnCriticalSection+0xe1
            ntdll!RtlEnterCriticalSection+0x94
            ntdll!LdrpAcquireLoaderLock+0x2c
            ntdll!LdrShutdownThread+0x64
            ntdll!RtlExitUserThread+0x3e
            KERNELBASE!FreeLibraryAndExitThread+0x4c
            combase!CRpcThreadCache::RpcWorkerThreadEntry+0x62
            KERNEL32!BaseThreadInitThunk+0x30
            ntdll!RtlUserThreadStart+0x42
    
            THREAD ffffe00003c46080  Cid 0a50.0a9c  Teb: 00007ff6b64e6000 Win32Thread: fffff90143713a90 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000041c1830  SynchronizationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`367ece40)
            ntdll!ZwWaitForSingleObject+0xa
            ntdll!RtlpWaitOnCriticalSection+0xe1
            ntdll!RtlEnterCriticalSection+0x94
            ntdll!LdrpAcquireLoaderLock+0x4c
            ntdll!LdrpFindOrMapDll+0x75d
            ntdll!LdrpLoadDll+0x394
            ntdll!LdrLoadDll+0xc6
            kernelbase!LoadLibraryExW+0x142
            kernelbase!LoadLibraryExA+0x26
            contoso!__delayLoadHelper2+0x2b
            contoso!_tailMerge_Winmm_dll+0x3f
            contoso!PolarityReverser::OnCompleted+0x28
            contoso!PolarityReverser::Reverse+0xf4
            contoso!ListItem::ReversePolarity+0x7e
            contoso!View::OnContextMenu+0x8
            contoso!View::WndProc+0x25e
            user32!UserCallWinProcCheckWow+0x13a
            user32!DispatchClientMessage+0xf8
            user32!__fnEMPTY+0x2d
            ntdll!KiUserCallbackDispatcherContinue
            user32!ZwUserMessageCall+0xa
            user32!RealDefWindowProcWorker+0x1e2
            user32!RealDefWindowProcW+0x52
            uxtheme!_ThemeDefWindowProc+0x33e
            uxtheme!ThemeDefWindowProcW+0x11
            user32!DefWindowProcW+0x1b6
            comctl32!CListView::WndProc+0x25e
            comctl32!CListView::s_WndProc+0x52
            user32!UserCallWinProcCheckWow+0x13a
            user32!SendMessageWorker+0xa72
            user32!SendMessageW+0x10a
            comctl32!CLVMouseManager::HandleMouse+0xd10
            comctl32!CLVMouseManager::OnButtonDown+0x27
            comctl32!CListView::WndProc+0x1a4186
            comctl32!CListView::s_WndProc+0x52
            user32!UserCallWinProcCheckWow+0x13a
            user32!DispatchMessageWorker+0x1a7
    
            THREAD ffffe0000462b8c0  Cid 0a50.0ac0  Teb: 00007ff6b64dc000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe0000462c980  NotificationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`201e9c40)
            ntdll!ZwWaitForSingleObject+0xa
            KERNELBASE!WaitForSingleObjectEx+0xa5
            contoso!CNetworkManager::ThreadProc+0x94
            KERNEL32!BaseThreadInitThunk+0x30
            ntdll!RtlUserThreadStart+0x42
    
            THREAD ffffe000046ad340  Cid 0a50.0b38  Teb: 00007ff6b64b6000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000049108c0  Thread
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`2563bc40)
            ntdll!ZwWaitForSingleObject+0xa
            KERNELBASE!WaitForSingleObjectEx+0xa5
            litware!CDiscovery::Uninitialize+0x8c
            litware!CApiInstance::~CApiInstance+0x48
            litware!CApiInstance::`scalar deleting destructor'+0x14
            litware!std::tr1::_Ref_count_obj<CApiInstance>::_Destroy+0x31
            litware!std::tr1::_Ref_count_base::_Decref+0x1b
            litware!std::tr1::_Ptr_base<CApiInstance>::_Decref+0x20
            litware!std::tr1::shared_ptr<CApiInstance>::{dtor}+0x20
            litware!std::tr1::shared_ptr<CApiInstance>::reset+0x3c
            litware!CSingleton<CApiInstance>::ReleaseRef+0x97
            litware!LitWareUninitialize+0xed
            fabrikam!CDoodadHelper::~CDoodadHelper+0x67
            fabrikam!_CRT_INIT+0xda
            fabrikam!__DllMainCRTStartup+0x1e5
            ntdll!LdrpCallInitRoutine+0x57
            ntdll!LdrpProcessDetachNode+0xfe
            ntdll!LdrpUnloadNode+0x77
            ntdll!LdrpDecrementNodeLoadCount+0xd0
            ntdll!LdrUnloadDll+0x34
            KERNELBASE!FreeLibrary+0x22
            combase!CClassCache::CDllPathEntry::CFinishObject::Finish+0x28
            combase!CClassCache::CFinishComposite::Finish+0x80
            combase!CClassCache::FreeUnused+0xda
            combase!CoFreeUnusedLibrariesEx+0x2c
            combase!CDllHost::MTAWorkerLoop+0x7d
            combase!CDllHost::WorkerThread+0x122
            combase!CRpcThread::WorkerLoop+0x4e
            combase!CRpcThreadCache::RpcWorkerThreadEntry+0x46
            KERNEL32!BaseThreadInitThunk+0x30
            ntdll!RtlUserThreadStart+0x42
    
            THREAD ffffe000046db8c0  Cid 0a50.0b50  Teb: 00007ff6b64aa000 Win32Thread: fffff9014370da90 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000046dcae0  NotificationEvent
                ffffe000046dd3c0  SynchronizationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForMultipleObjects+0x22b
            nt!ObWaitForMultipleObjects+0x1f8
            nt!NtWaitForMultipleObjects+0xde
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`21801c40)
            ntdll!ZwWaitForMultipleObjects+0xa
            KERNELBASE!WaitForMultipleObjectsEx+0xe1
            USER32!MsgWaitForMultipleObjectsEx+0x14e
            contoso!EventManagerImpl::MessageLoop+0x32
            contoso!EventManagerImpl::BackgroundProcessing+0x134
            ntdll!TppWorkpExecuteCallback+0x2eb
            ntdll!TppWorkerThread+0xa12
            KERNEL32!BaseThreadInitThunk+0x30
            ntdll!RtlUserThreadStart+0x42
    
            THREAD ffffe000049108c0  Cid 0a50.06cc  Teb: 00007ff6b6470000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000041c1830  SynchronizationEvent
    
            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13
            ntdll!ZwWaitForSingleObject+0xa
            ntdll!RtlpWaitOnCriticalSection+0xe1
            ntdll!RtlEnterCriticalSectionContended+0x94
            ntdll!LdrpAcquireLoaderLock+0x2c
            ntdll!LdrShutdownThread+0x64
            ntdll!RtlExitUserThread+0x3e
            KERNEL32!BaseThreadInitThunk+0x38
            ntdll!RtlUserThreadStart+0x42
    

    Since debugging is an exercise in optimism, let's ignore the stacks that didn't come out properly. If we can't make any headway, we can try to fix them, but let's be hopeful that the stacks that are good will provide enough information.

    Generally speaking, the deeper the stack, the more interesting it is, because uninteresting threads tend to be hanging out in their message loop or event loop, whereas interesting threads are busy doing something and have a complex stack trace to show for it.

    Indeed, one of the deep stacks belongs to thread 0a9c, and it also has a very telling section:

            ntdll!RtlpWaitOnCriticalSection+0xe1
            ntdll!RtlEnterCriticalSection+0x94
            ntdll!LdrpAcquireLoaderLock+0x4c
            ntdll!LdrpFindOrMapDll+0x75d
            ntdll!LdrpLoadDll+0x394
            ntdll!LdrLoadDll+0xc6
            kernelbase!LoadLibraryExW+0x142
            kernelbase!LoadLibraryExA+0x26
            contoso!__delayLoadHelper2+0x2b
            contoso!_tailMerge_Winmm_dll+0x3f
    

    The polarity reverser's completion handler is trying to load winmm via delay-load. That load request is waiting on a critical section, and it should be clear both from the scenario and the function names that the critical section it is trying to claim is the loader lock. In real life, I just proceeded with that conclusion, but but just for demonstration purposes, here's how we can double-check:

    0: kd> .thread ffffe00003c46080
    0: kd> kn
      *** Stack trace for last set context - .thread/.cxr resets it
     # Call Site
    00 nt!KiSwapContext+0x76
    01 nt!KiSwapThread+0x14c
    02 nt!KiCommitThreadWait+0x126
    03 nt!KeWaitForSingleObject+0x1cc
    04 nt!NtWaitForSingleObject+0xb1
    05 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`367ece40)
    06 ntdll!ZwWaitForSingleObject+0xa
    07 ntdll!RtlpWaitOnCriticalSection+0xe1
    08 ntdll!RtlEnterCriticalSection+0x94
    09 ntdll!LdrpAcquireLoaderLock+0x4c
    0a ntdll!LdrpFindOrMapDll+0x75d
    0b ntdll!LdrpLoadDll+0x394
    0c ntdll!LdrLoadDll+0xc6
    0d kernelbase!LoadLibraryExW+0x142
    0e kernelbase!LoadLibraryExA+0x26
    0f contoso!__delayLoadHelper2+0x2b
    10 contoso!_tailMerge_Winmm_dll+0x3f
    

    We need to grab the critical section passed to Rtl­Enter­Critical­Section, but since this is an x64 machine, the parameter was passed in registers, not on the stack, so we need to figure out where the rcx register got stashed.

    I'm going to assume that the same critical section is the first (only?) parameter to Rtlp­Wait­On­CriticalSection. I don't know this for a fact, but it seems like a reasonable guess. The guess might be wrong; we'll see.

    We disassemble the function look to see where it stashes rcx.

    0: kd> u ntdll!RtlpWaitOnCriticalSection
        mov     qword ptr [rsp+18h],rbx
        push    rbp
        push    rsi
        push    rdi
        push    r12
        push    r13
        push    r14
        push    r15
        mov     rax,qword ptr [ntdll!__security_cookie (000007ff`3099d020)]
        xor     rax,rsp
        mov     qword ptr [rsp+80h],rax
        mov   r14,qword ptr gs:[30h]
        xor     r12d,r12d
        lea     rax,[ntdll!LdrpLoaderLock (00007fff`d4f51cb8)]
        mov     r15d,r12d
        cmp     rcx,rax
        mov     ebp,edx
        sete    r15b
        mov     rbx,rcx // ⇐ Bingo
    

    Awesome, we can suck rbx out of the trap frame.

    0: kd> .trap ffffd000`367ece40
    rax=0000000000000000 rbx=00007fffd4f51cb8 rcx=000007f8136f2c2a
    rdx=0000000000000000 rsi=00000000000001e8 rdi=0000000000000000
    rip=000007f8136f2c2a rsp=000000000cf7f798 rbp=0000000000000000
     r8=000000000cf7f798  r9=0000000000000000 r10=0000000000000000
    r11=0000000000000344 r12=0000000000000000 r13=0000000000000000
    r14=000007f696870000 r15=000000007ffe0382
    iopl=0         nv up ei pl zr na po nc
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    ntdll!ZwWaitForSingleObject+0xa:
    000007f8`136f2c2a c3              ret
    

    Okay, let's see if that value in rbx pans out.

    0: kd> !cs 0x00007fff`d4f51cb8
    -----------------------------------------
    Critical section   = 0x00007fffd4f51cb8 (ntdll!LdrpLoaderLock+0x0)
    DebugInfo          = 0x00007fffd4f55228
    LOCKED
    LockCount          = 0x8
    WaiterWoken        = No
    OwningThread       = 0x0000000000000b38
    RecursionCount     = 0x1
    LockSemaphore      = 0x1A8
    SpinCount          = 0x0000000004000000
    

    Hooray, we confirmed that this is indeed the loader lock. I would have been surprised if it had been anything else! (If you had been paying attention, you would have noticed the lea rax,[ntdll!LdrpLoaderLock (00007fff`d4f51cb8)] in the disassembly which already confirms the value.)

    We also see that the owning thread is 0xb38. Here's its stack again:

            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`2563bc40)
            ntdll!ZwWaitForSingleObject+0xa
            KERNELBASE!WaitForSingleObjectEx+0xa5
            litware!CDiscovery::Uninitialize+0x8c
            litware!CApiInstance::~CApiInstance+0x48
            litware!CApiInstance::`scalar deleting destructor'+0x14
            litware!std::tr1::_Ref_count_obj<CApiInstance>::_Destroy+0x31
            litware!std::tr1::_Ref_count_base::_Decref+0x1b
            litware!std::tr1::_Ptr_base<CApiInstance>::_Decref+0x20
            litware!std::tr1::shared_ptr<CApiInstance>::{dtor}+0x20
            litware!std::tr1::shared_ptr<CApiInstance>::reset+0x3c
            litware!CSingleton<CApiInstance>::ReleaseRef+0x97
            litware!LitWareUninitialize+0xed
            fabrikam!CDoodadHelper::~CDoodadHelper+0x67
            fabrikam!_CRT_INIT+0xda
            fabrikam!__DllMainCRTStartup+0x1e5
            ntdll!LdrpCallInitRoutine+0x57
            ntdll!LdrpProcessDetachNode+0xfe
            ntdll!LdrpUnloadNode+0x77
            ntdll!LdrpDecrementNodeLoadCount+0xd0
            ntdll!LdrUnloadDll+0x34
            KERNELBASE!FreeLibrary+0x22
            combase!CClassCache::CDllPathEntry::CFinishObject::Finish+0x28
            combase!CClassCache::CFinishComposite::Finish+0x80
            combase!CClassCache::FreeUnused+0xda
            combase!CoFreeUnusedLibrariesEx+0x2c
            combase!CDllHost::MTAWorkerLoop+0x7d
            combase!CDllHost::WorkerThread+0x122
            combase!CRpcThread::WorkerLoop+0x4e
            combase!CRpcThreadCache::RpcWorkerThreadEntry+0x46
            KERNEL32!BaseThreadInitThunk+0x30
            ntdll!RtlUserThreadStart+0x42
    

    Reading from the bottom up, we see that this thread is doing some work on behalf of COM; specifically, it is freeing unused libraries. The fabrikam library presumably responded S_OK to Dll­Can­Unload­Now, so COM says, "Okay, then out you go."

    As part of DLL_PROCESS_DETACH processing, the C++ runtime library runs global destructors. The CDoodadHelper destructor calls into the Lit­Ware­Uninitialize function in litware.dll. That function decrements a reference count, and it appears that the reference count went to zero because it's destructing the CApi­Instance object. The destructor for that function calls CDiscovery::Uninitialize, and that function waits on a kernel object.

    The debugger was kind enough to tell us what the object is:

            THREAD ffffe000046ad340  Cid 0a50.0b38  Teb: 00007ff6b64b6000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe000049108c0  Thread
    

    It's a thread.

    Going back to the thread dump at the start, we also can see what thread ffffe000049108c0 is doing. Here it is again:

            nt!KiSwapContext+0x76
            nt!KiSwapThread+0x14c
            nt!KiCommitThreadWait+0x126
            nt!KeWaitForSingleObject+0x1cc
            nt!NtWaitForSingleObject+0xb1
            nt!KiSystemServiceCopyEnd+0x13
            ntdll!ZwWaitForSingleObject+0xa
            ntdll!RtlpWaitOnCriticalSection+0xe1
            ntdll!RtlEnterCriticalSectionContended+0x94
            ntdll!LdrpAcquireLoaderLock+0x2c
            ntdll!LdrShutdownThread+0x64
            ntdll!RtlExitUserThread+0x3e
            KERNEL32!BaseThreadInitThunk+0x38
            ntdll!RtlUserThreadStart+0x42
    

    That thread is trying to acquire the loader lock so it can send DLL_THREAD_DETACH notifications. But the loader lock is held by the Free­Library. Result: Deadlock, as the two threads are waiting for each other. (You can also see that thread 0xa8c is stuck in the same place because it too is trying to exit.)

    The underlying problem is that the Fabrikam DLL is waiting on a thread (indirectly via LitWare) while inside its own Dll­Main.

    The Fabrikam code could avoid this problem by calling Lit­Ware­Uninitialize when its last object is destroyed rather than when the DLL is unloaded. (Of course, it also has to remember to call Lit­Ware­Initialize when its first object is created.)

  • The Old New Thing

    Why don't the shortcuts I put in the CSIDL_COMMON_FAVORITES folder show up in the Favorites menu?

    • 25 Comments

    A customer created some shortcuts in the CSIDL_COMMON_FAVORITES folder, expecting them to appear in the Favorites menu for all users. Instead, they appeared in the Favorites menu for no users. Why isn't CSIDL_COMMON_FAVORITES working?

    The CSIDL_COMMON_FAVORITES value was added at the same time as the other CSIDL_COMMON_* values, and its name strongly suggests that its relationship to CSIDL_FAVORITES is the same as the relationship between CSIDL_COMMON_STARTMENU and CSIDL_STARTMENU, or between CSIDL_COMMON_PROGRAMS and CSIDL_PROGRAMS, or between CSIDL_COMMON_DESKTOP­DIRECTORY and CSIDL_DESKTOP­DIRECTORY.

    That suggestion is a false one.

    In fact, CSIDL_COMMON_FAVORITES is not hooked up to anything. It's another of those vestigial values that got created with the intent of actually doing something but that thing never actually happened. I don't think any version of Internet Explorer ever paid any attention to that folder. Maybe the designers decided that it was a bad idea and cut the feature. Maybe it was an oversight. Whatever the reason, it's just sitting there wasting space.

    Sorry for the fake-out.

    Exercise: Another customer wanted to know why creating a %ALL­USERS­PROFILE%\Microsoft\Internet Explorer\Quick Launch directory and putting shortcuts into it did not result in those shortcuts appearing in every user's Quick Launch bar. Explain.

  • The Old New Thing

    The economic inefficiency of gift-giving

    • 19 Comments

    Economist Joel Waldfogel explains why gift-giving is bad for the economy, and why a charity gift card is the best luxury gift of all.

    He goes into more detail in his new book, Scroogenomics, which you can buy somebody for Christmas just to tell Waldfogel where he can stick it. ("In the bank!" he'll say as he heads out with his royalty check.)

    Related: Economist Tim Harford writes an advice column called Dear Economist for the Financial Times. But instead of applying economic theory to economic problems, he applies economic theory to personal problems.

    As Harford himself explains, "Every advice columnist needs a persona, and for Dear Economist it was blunt, rude, and rather fond of the latest economic research."

  • The Old New Thing

    Bowling coming to Bellevue, and given the location, it's naturally upscale

    • 7 Comments

    The Lincoln Square mall in Bellevue will have a new tenant: An upscale bowling all^H^H^Hlounge. Expected to open before the Christmas holiday season, there will be two bars, full-service dining, lots of big plasma screens, all the stuff that makes bowling better. The lounge be positioned on the second floor, beneath Parlor Billiards, a business which by a fantastic coincidence has exactly the same business model as the bowling all^H^H^Hlounge, but with billiards instead of bowling. By Christmastime, the 23,900 square foot venue will be the site of strikes, splits, and, no doubt, lots of failed pick-up attempts.

  • The Old New Thing

    The migration continues

    • 4 Comments
    Hey folks, this blog is moving to a new home. It'll take me a while to set up shop there, though, so please be patient. Some time after the new year, the content will likely migrate to the new location.
Page 377 of 436 (4,359 items) «375376377378379»