Python Ctypes - loading dll throws OSError: [WinError 193] %1 is not a valid Win32 application

Solution 1:

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
  • Was (mistakenly) overwritten (or messed up with)
  • Is corrupt because of filesystem problem
  • Many many more

2 main usecases lead to this error:

  1. Attempting to run a file which is not an .exe ([SO]: OSError: [WinError 193] %1 is not a valid Win32 application)
  2. 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.

main00.c:

#include <stdio.h>
#include <Windows.h>


int main()
{
    DWORD gle = 0;
    HMODULE hMod = LoadLibraryA(".\\dll00.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
[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]> :: Build for 064bit
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul

[prompt]> dir /b
code00.py
dll00_v0.c
main00.c

[prompt]> cl /nologo main00.c  /link /NOLOGO /OUT:main00_064.exe
main00.c

[prompt]> :: Creating an invalid .dll
[prompt]> echo garbage> dll00.dll

[prompt]> dir /b
code00.py
dll00.dll
dll00_v0.c
main00.c
main00.obj
main00_064.exe

[prompt]> main00_064.exe
LoadLibrary failed: 193 (0x000000C1)

As seen, I created a file dll00.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:

  • 064bit process attempting to load a 032bit .dll
  • 032bit process attempting to load a 064bit .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.

code00.py:

#!/usr/bin/env python3

import sys
import os
import ctypes as ct


DLL_BASE_NAME = "dll00"


def main(*argv):
    dll_name = os.path.join(os.path.abspath(os.path.dirname(__file__)), (argv[0] if argv else DLL_BASE_NAME) + ".dll")
    print("Attempting to load: [{0:s}]".format(dll_name))
    dll00 = ct.CDLL(dll_name)
    func00 = dll00.dll00Func00
    func00.restype = ct.c_int

    res = func00()
    print("{0:s} returned {1:d}".format(func00.__name__, res))


if __name__ == "__main__":
    print("Python {0:s} {1:03d}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))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Output:

[prompt]> :: dll00.dll still contains garbage
[prompt]>
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.09_test0\Scripts\python.exe" code00.py
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] 064bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00.dll]
Traceback (most recent call last):
  File "code00.py", line 25, in <module>
    rc = main(*sys.argv[1:])
  File "code00.py", line 14, in main
    dll00 = ct.CDLL(dll_name)
  File "c:\Install\pc064\Python\Python\03.07.09\lib\ctypes\__init__.py", line 364, 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.

dll00_v0.c:

#include <inttypes.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif


DLL00_EXPORT_API size_t dll00Func00()
{
    return sizeof(void*);
}

Output:

[prompt]> :: Still building for 064bit from previous vcvarsall call
[prompt]>
[prompt]> cl /nologo /DDLL dll00_v0.c  /link /NOLOGO /DLL /OUT:dll00_064.dll
dll00_v0.c
   Creating library dll00_064.lib and object dll00_064.exp

[prompt]>
[prompt]> :: Build for 032bit
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x86
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.40
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x86'

[prompt]> cl /nologo /DDLL dll00_v0.c  /link /NOLOGO /DLL /OUT:dll00_032.dll
dll00_v0.c
   Creating library dll00_032.lib and object dll00_032.exp

[prompt]> dir /b *.dll
dll00.dll
dll00_032.dll
dll00_064.dll

[prompt]>
[prompt]> :: Python 064bit
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.09_test0\Scripts\python.exe" code00.py dll00_064
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] 064bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_064.dll]
dll00Func00 returned 8

Done.

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.09_test0\Scripts\python.exe" code00.py dll00_032
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] 064bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_032.dll]
Traceback (most recent call last):
  File "code00.py", line 25, in <module>
    rc = main(*sys.argv[1:])
  File "code00.py", line 14, in main
    dll00 = ct.CDLL(dll_name)
  File "c:\Install\pc064\Python\Python\03.07.09\lib\ctypes\__init__.py", line 364, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application

[prompt]>
[prompt]> :: Python 032bit
[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.09_test0\Scripts\python.exe" code00.py dll00_032
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:01:55) [MSC v.1900 32 bit (Intel)] 032bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_032.dll]
dll00Func00 returned 4

Done.

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.09_test0\Scripts\python.exe" code00.py dll00_064
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:01:55) [MSC v.1900 32 bit (Intel)] 032bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_064.dll]
Traceback (most recent call last):
  File "code00.py", line 25, in <module>
    rc = main(*sys.argv[1:])
  File "code00.py", line 14, in main
    dll00 = ct.CDLL(dll_name)
  File "c:\Install\pc032\Python\Python\03.07.09\lib\ctypes\__init__.py", line 364, 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, dll00*.dll depends on dll01*.dll.
Only exemplifying for 032bit (as this is the current build environment set by previous operation).

dll01.h:

#if defined(_WIN32)
#  if defined(DLL01_EXPORTS)
#    define DLL01_EXPORT_API __declspec(dllexport)
#  else
#    define DLL01_EXPORT_API __declspec(dllimport)
#  endif
#else
#  define DLL01_EXPORT_API
#endif


DLL01_EXPORT_API void dll01Func00();

dll01.c:

#include <stdio.h>
#define DLL01_EXPORTS
#include "dll01.h"


void dll01Func00()
{
    printf("In [%s]\n", __FUNCTION__);
}

dll00_v1.c: (modified dll00_v0.c):

#include <inttypes.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

#include "dll01.h"


DLL00_EXPORT_API size_t dll00Func00()
{
    dll01Func00();
    return sizeof(void*);
}

Output:

[prompt]> :: Still building for 032bit from previous vcvarsall call
[prompt]>
[prompt]> cl /nologo /DDLL dll01.c  /link /NOLOGO /DLL /OUT:dll01_032.dll
dll01.c
   Creating library dll01_032.lib and object dll01_032.exp

[prompt]> cl /nologo /DDLL dll00_v1.c  /link /NOLOGO /DLL /OUT:dll00_032.dll
dll00_v1.c
   Creating library dll00_032.lib and object dll00_032.exp
dll00_v1.obj : error LNK2019: unresolved external symbol __imp__dll01Func00 referenced in function _dll00Func00
dll00_032.dll : fatal error LNK1120: 1 unresolved externals

[prompt]>
[prompt]> cl /nologo /DDLL dll00_v1.c  /link /NOLOGO /DLL /OUT:dll00_032.dll dll01_032.lib
dll00_v1.c
   Creating library dll00_032.lib and object dll00_032.exp

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.09_test0\Scripts\python.exe" code00.py dll00_032
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:01:55) [MSC v.1900 32 bit (Intel)] 032bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_032.dll]
In [dll01Func00]
dll00Func00 returned 4

Done.

[prompt]> :: Messing up dll01_032.dll
[prompt]> echo garbage> dll01_032.dll

[prompt]> "e:\Work\Dev\VEnvs\py_pc032_03.07.09_test0\Scripts\python.exe" code00.py dll00_032
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:01:55) [MSC v.1900 32 bit (Intel)] 032bit on win32

Attempting to load: [e:\Work\Dev\StackOverflow\q057187566\dll00_032.dll]
Traceback (most recent call last):
  File "code00.py", line 25, in <module>
    rc = main(*sys.argv[1:])
  File "code00.py", line 14, in main
    dll00 = ct.CDLL(dll_name)
  File "c:\Install\pc032\Python\Python\03.07.09\lib\ctypes\__init__.py", line 364, 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 dll01_032.dll, I would have build it for 064bit, 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

Solution 2:

As @CristiFati stated this is occuring because

1)64bit process attempting to load a 32bit .dll

2)32bit process attempting to load a 64bit .dll

Solution:

--> I also encountered the same problem and noticed that my gcc compiler was producing 32-bit compiled files instead of 64-bit.So I changed the compiler which produces 64-bit files.

--> you can check your compiled file(.exe) is 64 or 32 bit by -->right click --> properties--> compatablity -->check the compatibilty mode option-->select the dropdown if you see windows xp in the list then your compiler is producing 32-bit files if you dont see windows xp then your compiler is producing 64-bit files.