mutex is like a lock that is shared between processes. So if we run two instances of the following program at the same time, the second instance will hang/wait 10 seconds before acquiring the mutex.

#include "stdafx.h"

#include <Windows.h>

#include <synchapi.h>


int main(int argc, _TCHAR* argv[])
{
    WCHAR* cacheMutexName = L"HelloWorldMutex";
    HANDLE handle = ::CreateMutex(nullptr, FALSE, cacheMutexName);

    DWORD result = WaitForSingleObject(handle, INFINITE);

    switch (result)
    {
    case WAIT_OBJECT_0:
        printf("The thread got ownership of the mutex.\r\n");
        break;
    case WAIT_ABANDONED:
        printf("The thread got ownership of an abandoned mutex.\r\n");
        break;
    }

    // Countdown.

    for (int i = 10; i > 0; i--)
    {
        wprintf(L"%d\r\n", i);
        Sleep(1000);
    }
    wprintf(L"0\r\n");

    ReleaseMutex(handle);
    printf("Mutex released.\r\n");

    return 0;
}

In Win32, once you get the mutex, you can distinguish between two different cases. The first case is WAIT_OBJECT_0 and it means the previous owner released the mutex properly by calling ReleaseMutex() and the second case is WAIT_ABANDONED and it means the previous owner terminated without calling ReleaseMutex().

To reproduce the WAIT_ABANDONED case with the sample program, press CTRL + C in the first instance before the countdown hits zero.

When using WinDbg, during live debugging or during dump analysis, the !handle extension comes very handy.

Just get the handle value:

0:000> dv
 argc = 0n1
 argv = 0x010f6f28
 handle = 0x00000038
 result = 0xcccccccc
 cacheMutexName = 0x003f5858 "HelloWorldMutex"

And print all the handle info:

0:000> !handle 0x00000038 f
Handle 38
 Type Mutant
 Attributes 0
 GrantedAccess 0x1f0001:
 Delete,ReadControl,WriteDac,WriteOwner,Synch
 QueryState
 HandleCount 3
 PointerCount 98306
 Name \Sessions\BaseNamedObjects\HelloWorldMutex
 Object Specific Information
 Mutex is Owned
 Mutant Owner 11ac.1628

Now we know 11ac.1628 is the owner of the mutex:

If we print the call stack for each thread in the second instance of the program:

0:000> ~* kcn

.  0  Id: 29f8.778 Suspend: 1 Teb: 7f15c000 Unfrozen
 # 
00 ntdll!NtWaitForSingleObject
01 KERNELBASE!WaitForSingleObjectEx
02 KERNELBASE!WaitForSingleObject
03 MutexExample!main
04 MutexExample!__tmainCRTStartup
05 MutexExample!mainCRTStartup
06 KERNEL32!BaseThreadInitThunk
07 ntdll!__RtlUserThreadStart
08 ntdll!_RtlUserThreadStart

#  1  Id: 29f8.ec4 Suspend: 1 Teb: 7f159000 Unfrozen
 # 
00 ntdll!DbgBreakPoint
01 ntdll!DbgUiRemoteBreakin
02 KERNEL32!BaseThreadInitThunk
03 ntdll!__RtlUserThreadStart
04 ntdll!_RtlUserThreadStart

We do not see any 11ac.1628 thread.

But if we print the call stacks of the first process intance:

0:000> ~* kcn

.  0  Id: 11ac.1628 Suspend: 1 Teb: 7fe7d000 Unfrozen
 # 
00 ntdll!NtDelayExecution
01 KERNELBASE!SleepEx
02 KERNELBASE!Sleep
03 MutexExample!main
04 MutexExample!__tmainCRTStartup
05 MutexExample!mainCRTStartup
06 KERNEL32!BaseThreadInitThunk
07 ntdll!__RtlUserThreadStart
08 ntdll!_RtlUserThreadStart

#  1  Id: 11ac.1c48 Suspend: 1 Teb: 7fe7a000 Unfrozen
 # 
00 ntdll!DbgBreakPoint
01 ntdll!DbgUiRemoteBreakin
02 KERNEL32!BaseThreadInitThunk
03 ntdll!__RtlUserThreadStart
04 ntdll!_RtlUserThreadStart

Then we see the 11ac.1628 thread.

In fact 0x11ac and 0x29f8 match the PID of the processes:

C:\>tlist
4524 MutexExample.exe
10744 MutexExample.exe

Happy handle debugging!