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.
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)