Mentioning [Python.Docs]: ctypes - A foreign function library for Python (although this doesn't have very much to do with it) just in case.
The underlying error is ERROR_BAD_EXE_FORMAT (193, 0xC1). Check it in [MS.Docs]: System Error Codes (0-499). It's a general Win error (not related to Python). In the current case (related to Python), the exception is a (Python) wrapper over it.
1. The error
The error message is confusing (especially because of %1 placeholder). For more details on that, check [SO]: Why is %1 rarely substituted in “%1 is not a valid Win32 application.”.
This error occurs when Win tries to load what it thinks it's an executable (PE) image (.exe, .dll, ...), but it actually isn't. There's a variety of situations when this is encountered (Googleing the error, would yield lots of results).
There are a bunch of possible reasons for this to happen when the image is loaded from a file (existing and readable, otherwise the error would differ - look at one of the bullets at the answer end):
- Was downloaded and the download is incomplete
- Is corrupt because of filesystem problem
- Was mistakenly overwritten
- Many many more
2 main usecases lead to this error:
- Attempting to run a file which is not an .exe ([SO]: OSError: [WinError 193] %1 is not a valid Win32 application)
- Trying to load a .dll in a process (running .exe). This is the one that I'm going to focus on
Below, it's an example of a dummy executable attempting to load a .dll.
code0.c:
#include <stdio.h>
#include <Windows.h>
int main() {
DWORD gle = 0;
HMODULE hMod = LoadLibraryA(".\\dll0.dll");
if (hMod == NULL) {
gle = GetLastError();
printf("LoadLibrary failed: %d (0x%08X)\n", gle, gle);
} else {
FreeLibrary(hMod);
}
return gle;
}
Output:
- Note: I'll be reusing this cmd console, even if the copy / paste snippets will be scattered across the answer.
As a side note, I don't know why this snippet is messed up (as opposed to others), the best (not perfect) workaround I found is to split it in 3
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057187566]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.22
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
[prompt]> dir /b
code0.c
dll0.c
script0.py
[prompt]> cl /nologo code0.c /link /NOLOGO /OUT:code0_064.exe
code0.c
[prompt]> :: Creating an invalid dll
[prompt]> echo garbage> dll0.dll
[prompt]> dir /b
code0.c
code0.obj
code0_064.exe
dll0.c
dll0.dll
script0.py
[prompt]> code0_064.exe
LoadLibrary failed: 193 (0x000000C1)
As seen, I created a file dll0.dll containing the text "garbage", so it's a .dll file with invalid contents.
The most common case for this error, is an architecture mismatch:
- 64bit process attempting to load a 32bit .dll
- 32bit process attempting to load a 64bit .dll
In any of the above 2 cases, even if the .dll contains a valid image (for a different architecture), it's still invalid from the current process PoV. For things to run OK, the 2 involved CPU architectures must match (1).
2. Python context
CTypes does the same thing when loading a .dll: it calls [MS.Docs]: LoadLibraryW function on the .dll name.
So this is the exact same case for the Python process where CTypes tries to load the .dll in.
script0.py:
#!/usr/bin/env python3
import sys
import os
import ctypes
DLL_BASE_NAME = "dll0"
def main(args):
dll_name = os.path.join(os.path.abspath(os.path.dirname(__file__)), (args[0] if args else DLL_BASE_NAME) + ".dll")
print("Attempting to load: [{0:s}]".format(dll_name))
dll0 = ctypes.CDLL(dll_name)
func0 = dll0.dll0Func0
func0.restype = ctypes.c_int
res = func0()
print("{0:s} returned {1:d}".format(func0.__name__, res))
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(sys.argv[1:])
print("\nDone.")
Output:
[prompt]> :: dll0.dll still contains garbage
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.03_test0\Scripts\python.exe" script0.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0.dll]
Traceback (most recent call last):
File "script0.py", line 24, in <module>
main(sys.argv[1:])
File "script0.py", line 14, in main
dll0 = ctypes.CDLL(dll_name)
File "c:\install\pc064\python\python\03.07.03\Lib\ctypes\__init__.py", line 356, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application
Here's an example for #1 (from above), which attempts all 4 combinations.
dll0.c:
#include <inttypes.h>
#if defined(_WIN32)
# define DLL0_EXPORT_API __declspec(dllexport)
#else
# define DLL0_EXPORT_API
#endif
DLL0_EXPORT_API size_t dll0Func0() {
return sizeof(void*);
}
Output:
[prompt]> :: Still building for 64bit from previous vcvarsall call
[prompt]>
[prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0_064.dll
dll0.c
Creating library dll0_064.lib and object dll0_064.exp
[prompt]>
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x86
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.22
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x86'
[prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0_032.dll
dll0.c
Creating library dll0_032.lib and object dll0_032.exp
[prompt]> dir /b *.dll
dll0.dll
dll0_032.dll
dll0_064.dll
[prompt]>
[prompt]> :: Python 64bit
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.03_test0\Scripts\python.exe" script0.py dll0_064
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_064.dll]
dll0Func0 returned 8
Done.
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.03_test0\Scripts\python.exe" script0.py dll0_032
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_032.dll]
Traceback (most recent call last):
File "script0.py", line 24, in <module>
main(sys.argv[1:])
File "script0.py", line 14, in main
dll0 = ctypes.CDLL(dll_name)
File "c:\install\pc064\python\python\03.07.03\Lib\ctypes\__init__.py", line 356, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application
[prompt]>
[prompt]> :: Python 32bit
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.03_test0\Scripts\python.exe" script0.py dll0_032
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_032.dll]
dll0Func0 returned 4
Done.
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.03_test0\Scripts\python.exe" script0.py dll0_064
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_064.dll]
Traceback (most recent call last):
File "script0.py", line 24, in <module>
main(sys.argv[1:])
File "script0.py", line 14, in main
dll0 = ctypes.CDLL(dll_name)
File "c:\install\pc032\python\python\03.07.03\Lib\ctypes\__init__.py", line 356, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application
3. Bonus
In the above examples, the .dll was loaded "on demand" by explicitly calling LoadLibrary (or LoadLibraryEx).
The other case is when a .exe or .dll depends on (was linked against) another .dll, and loads it automatically when itself is being loaded (although I'm almost certain that LoadLibrary - or maybe a lower level function - is automatically called under the hood on the dependent .dll).
In the example below, dll0*.dll depends on dll1*.dll. Only exemplifying for 32bit (as this is the current build environment set by previous operation).
dll1.h:
#if defined(_WIN32)
# if defined(DLL1_EXPORTS)
# define DLL1_EXPORT_API __declspec(dllexport)
# else
# define DLL1_EXPORT_API __declspec(dllimport)
# endif
#else
# define DLL1_EXPORT_API
#endif
DLL1_EXPORT_API void dll1Func0();
dll1.c:
#include <stdio.h>
#define DLL1_EXPORTS
#include "dll1.h"
void dll1Func0() {
printf("In [%s]\n", __FUNCTION__);
}
dll0.c (modified):
#include <inttypes.h>
#if defined(_WIN32)
# define DLL0_EXPORT_API __declspec(dllexport)
#else
# define DLL0_EXPORT_API
#endif
#include "dll1.h"
DLL0_EXPORT_API size_t dll0Func0() {
dll1Func0();
return sizeof(void*);
}
Output:
[prompt]> :: Still building for 32bit from previous vcvarsall call
[prompt]>
[prompt]> cl /nologo /DDLL dll1.c /link /NOLOGO /DLL /OUT:dll1_032.dll
dll1.c
Creating library dll1_032.lib and object dll1_032.exp
[prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0_032.dll
dll0.c
Creating library dll0_032.lib and object dll0_032.exp
dll0.obj : error LNK2019: unresolved external symbol __imp__dll1Func0 referenced in function _dll0Func0
dll0_032.dll : fatal error LNK1120: 1 unresolved externals
[prompt]>
[prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0_032.dll dll1_032.lib
dll0.c
Creating library dll0_032.lib and object dll0_032.exp
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.03_test0\Scripts\python.exe" script0.py dll0_032
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_032.dll]
In [dll1Func0]
dll0Func0 returned 4
Done.
[prompt]> :: Messing up dll1_032.dll
[prompt]> echo garbage> dll1_032.dll
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.03_test0\Scripts\python.exe" script0.py dll0_032
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll0_032.dll]
Traceback (most recent call last):
File "script0.py", line 24, in <module>
main(sys.argv[1:])
File "script0.py", line 14, in main
dll0 = ctypes.CDLL(dll_name)
File "c:\install\pc032\python\python\03.07.03\Lib\ctypes\__init__.py", line 356, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application
Stating the obvious: Same error would occur if instead of writing garbage data into dll1_032.dll, I would have build it for 64bit, but I chose this variant as it's shorter.
4. Conclusions
Everything that I'll state in each of the next bullets, also applies to the ones that follow it.
- In the examples above, the error occurred when the corruption was in the very .dll being loaded, or in one of its direct dependents (level 1 of indirection). It's not hard to figure out that applying the same principle multiple times, the behavior wouldn't change, so it's valid for any level of indirection.
Imagine a .dll that depends on several other .dlls, and each of those depends in turn on several others, and so on ... . That is called a dependency tree. So no matter where in the tree this error will occur, it will be propagated up to the root node (which is the .dll)
- The dependency tree propagation applies to other errors as well. Another one that it's widely encountered is ERROR_MOD_NOT_FOUND (126, 0x7E). It means that the .dll with the specified name (restating: or any other .dll that it (recursively) depends on) was not found.
As a side note, in order to check a .dll (or .exe) dependencies, use Dependency Walker (newer [GitHub]: lucasg/Dependencies) or dumpbin (part of VStudio installation), or as a matter of fact, any tool that is capable of getting PE dependency information
- Everything discussed also applies:
- If the .dll is an extension module (.pyd) that is being imported
- If the .dll is being loaded as a result of another module being imported
- Everything discussed also applies to Nix systems, the errors (and corresponding messages), obviously differ