Suppress “The program can’t start because X.dll is missing” error popup

I have a Python program which uses os.system to execute various commands. (It can’t use subprocess because it has to be backward compatible all the way to Python 2.0.)

On Windows, sometimes the command references DLLs in an unusual directory, and so I get the infamous “The program can’t start because X.dll is missing” error popup.

Windows error popup with title "cl.exe - System Error" and text "The program can't start because mspdb80.dll is missing from your computer. Try reinstalling the program to fix this problem."

My question is not about how to make the command find all its DLLs. I already know how to do that. What I want to know is, how do I tell Windows not to show this dialog box when a DLL is missing? Instead, the child process should print the error message to stderr (which has been redirected to a file within the os.system invocation) and exit unsuccessfully (causing os.system to return an error code). That way my program could capture the error and report it in its own way, rather than hanging until someone comes along to click OK.

MSDN is normally my friend, but this time I get nothing but advice on how to cope with specific missing DLLs, which is nice and all but not what I need this time.

To reiterate, this is an extreme-backward-compatibility situation: I need a solution that works with Python 2.7 or any older version all the way back to 2.0. It also needs to work on all still-popular versions of Windows (XP, Vista, 7, 8). Working with even older Windows is highly desirable but not 100% required. Further, third-party modules and helper programs written in any other language are not an option. (I suppose a .BAT file would be okay, if that’s the only way to do it.)

Best answer

The dialog box can be disabled for the calling process with SetErrorMode. However, you have to read the LoadLibrary documentation to discover that “missing DLL at load time” qualifies as one of the “critical errors” covered by SEM_FAILCRITICALERRORS.

The error mode inherits to child processes, as long as they are not created with CREATE_DEFAULT_ERROR_MODE, and it appears that CMD.EXE does not set that flag when it creates subprocesses. So setting the error mode on startup in my Python script does in fact suppress the dialog box in the situation I care about…

if sys.platform == 'win32':
    try:
        import ctypes
        # SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX
        ctypes.windll.kernel32.SetErrorMode(0x0001|0x0002|0x8000)
    except:
        pass

This is not an optimal solution: the subprocess terminates with a particular error code (0xC0000135 — not actually documented as “missing DLL”, but evidently so from what comes up when you search on that number) but the details — like which DLL is missing — are dropped on the floor. I still hope to find a setting somewhere that makes the loader report the details to stderr.