For the most part you are correct - Critical Section objects are preferred over Win32 Mutex objects for all the reasons you listed. std::mutex from the C++ library is just another variation of critical sections as well. Same for pthread_mutex_t instances.
The only case I can think of for using a Win32 mutex between thread of the same process instead of a simpler critical section is with a "named" mutex. With CRITICAL_SECTIONs, both threads need to share a pointer to the CRITICAL_SECTION.
But let's say you have two disparate pieces of code that are in different components and threads. Because of the way the code is presently architected (e.g. separate DLLs and libraries), there's really no easy way to share a CRITICAL_SECTION pointer between the two code paths without doing a lot of plumbing between the two.
A named mutex solves that. The name is a string you pass as the 3rd parameter to CreateMutex.
Both components can do something like the following when they initialize
HANDLE hMutex = CreateMutex(nullptr, FALSE, L"App_Resource_Thing");
Now they have unique handles to the SAME mutex object. Then they can both enter the mutex with their handle:
DWORD dwWait = WaitForSingleObject(hMutex, INFINITE);
if ((dwWait == 0) || (dwWait == WAIT_ABANDONED))
{
// operate on shared resource
// release mutex to whoever wants it next
ReleaseMutex(hMutex);
}
Now with named mutexes comes another issue - security. With the sample above, another application, assuming it knows the name of the mutex, can come along and lock it forever That will effectively freeze the other app. Or more generically, just another instance of your own EXE running would conflict with the first instance if using a hardcoded name. Using the SECURITY_DESCRIPTOR and/or generating a unique name for the mutex at runtime that can't be guessed by other rogue processes would help mitigate that issue.