pythonwindowsmutex

Windows CreateMutexW using Python not working


I'm writing a Python application that connects to an external device. I want to enable the user to operate multiple devices simultaneously by running multiple instances of the application, but each device may only connect to one application instance. This calls for a mutex keyed to the device's serial number. With the help of ChatGPT, I came up with the following minimal example (without the serial number):

import ctypes
import sys

ERROR_ALREADY_EXISTS = 183

mutex = ctypes.windll.kernel32.CreateMutexW(None, 1, "my-app\\1234")
err = ctypes.GetLastError()
print(f'Error code received: {err}')

if err == ERROR_ALREADY_EXISTS:
    print("Another instance is already using the device.")
    sys.exit(0)

input("Program running. Press Enter to exit...")

When I open two command windows and run this script in each one, neither one errors out and both give an err value of 3. I checked the Microsoft documentation for CreateMutexW and for the error codes, and I can't figure out what I'm doing wrong.


Solution

  • As mentioned in comments, use the Global\ namespace for a mutex that can be seen across all processes.

    Here's a pedantic test example with type and error checking of the exposed Win32 functions:

    import ctypes as ct
    import ctypes.wintypes as w
    
    ERROR_ALREADY_EXISTS = 183
    
    # Error checking functions.  Converts bad results to exceptions.
    
    def check_null_handle(result, func, args):
        if result is None:
            # ct.WinError() raises an appropriate OSError.
            raise ct.WinError(ct.get_last_error())
        return result
    
    def zerocheck(result, func, args):
        if result == 0:
            raise ct.WinError(ct.get_last_error())
    
    # `use_last_error=True` captures GetLastError() immediately
    # after type ctypes call completes.
    kernel32 = ct.WinDLL('kernel32', use_last_error=True)
    
    # Explicit type declaration of arguments and return value.
    # Also explicit error checking of return value.
    CreateMutex = kernel32.CreateMutexW
    CreateMutex.argtypes = ct.c_void_p, w.BOOL, w.LPCWSTR
    CreateMutex.restype = w.HANDLE
    CreateMutex.errcheck = check_null_handle
    CloseHandle = kernel32.CloseHandle
    CloseHandle.argtypes = w.HANDLE,
    CloseHandle.restype = w.BOOL
    CloseHandle.errcheck = zerocheck
    
    try:
        mutex = CreateMutex(None, True, "Global\\1234")
    except OSError as e:
        print(e)
    else:
        try:
            if ct.get_last_error() == ERROR_ALREADY_EXISTS:
                print("Another instance is already using the device.")
            else:
                input("Program running. Press Enter to exit...")
        finally:
            CloseHandle(mutex)