Vitality
Vitality

Reputation: 21475

Python: module not found (OSError: [WinError 126]) when loading a DLL using FFTW

I'm creating a dll using Visual Studio 2015 and FFTW. I'm then loading the dll under Python using

libc = cdll.LoadLibrary('D:\\Library_C\\x64\\Release\\Library_C.dll')

Under Visual Studio, I'm linking against the FFTW .lib libraries, of course (libfftw3-3.lib, libfftw3f-3.lib, libfftw3l-3.lib). Everything compiles fine. The dll is also created.

However, Python unespectedly says that the module cannot be found.

If I remove any invocation to FFTW functions from within the compiled code, Python finds the module.

How can I solve the problem?

I would be grateful for any help.

EDIT

The error code I received was

OSError: [WinError 126]

Googling around, I have noticed that such an error code is quite common in Python when loading external dlls.

Upvotes: 0

Views: 503

Answers (2)

Stephan Schlecht
Stephan Schlecht

Reputation: 27106

Most likely it is caused due to the fact that the libfftw3 dll(s) can't be found.

For a short test, the Python program and all DLLs can simply be stored in the same directory. Then start your command prompt and change to this directory(!) and start your Python program from the command line by typing python <your.py>. So all required DLLs are in the current directory and can be found.

Please note that your own dll is not statically linked to the fftw libraries when using import libraries (most likely). So the libfftw3 dlls must be found at runtime. There are several ways to do this.

DLL Search Order

There is a Microsoft document about the DLL search order:

https://learn.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order

For example, there are the following possibilities:

  • the current directory (if you are working from the command line)

  • adding the directory in which the DLLs are located to the environment variable PATH

  • and as @JackOLantem mentions, you can also load implicit dependent libraries directly from your Python program

Instructions

If my comment above doesn't solve the problem, here is a step-by-step guide with a simple test case. I have tried to keep the test case as close as possible to your example.

Create Import Libraries

If you are working on a 64-bit machine with a 64-bit Python installation, don't forget to use the /machine:x64 argument:

lib /machine:x64 /def:libfftw3-3.def

Create your own dll which references some libfftw3 functions, e.g. like so:

#include "windows.h"
#include "fftw3.h"

#define NUM_POINTS 128


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

__declspec(dllexport) char *plan() {
    fftw_complex signal[NUM_POINTS];
    fftw_complex result[NUM_POINTS];

    fftw_plan plan = fftw_plan_dft_1d(NUM_POINTS,
        signal,
        result,
        FFTW_FORWARD,
        FFTW_ESTIMATE);

    return fftw_sprint_plan(plan);
}

In Visual Studio switch to x64 configuration to build a 64-bit dll.

  • under Configuration Properties/Linker/Input under Additional Dependencies add libttfw3-3.lib.

  • under Configuration Properties/Linker/General add the path where the .lib resides to Additional Library Directories.

Python Test

The Python test program would look like this:

import ctypes

libc = ctypes.cdll.LoadLibrary('C:\\Users\\stephan\\Documents\\pyfftw\\Library_C\\x64\\Debug\\Library_C.dll')

libc.plan.restype = ctypes.c_char_p

print(libc.plan())

One doesn't need to use an absolute path. If you put the Library_C.dll also into the folder where the .py file is located you can just write:

libc = ctypes.cdll.LoadLibrary('Library_C.dll')

The result is the same. Also of course the standard search order for Windows dlls apply if you prefer a different solution.

Result

After you copy the libfftw3-3.dll into the same directory where the .py file resides you can call it by:

cd <dir-with-python-program-and-dlls>
python test.py

The result is something like:

b'(dft-ct-dit/8\n  (dftw-direct-8/12 "t3fv_8_avx")\n  (dft-direct-16-x8 "n2fv_16_avx"))'

Upvotes: 1

Vitality
Vitality

Reputation: 21475

Important

The steps by Stephan Schlecht are necessary before the below ones.

Solution

The Dependencies tool was quite helpful for me. It clearly indicated the dependency on libfftw3-3.dll.

Quoting Stephan

Most likely it is caused due to the fact that the libfftw3 dll(s) can't be found.

For a quick test just put it in the same directory where your .py files resides.

Please note that your own dll is not statically linked to the fftw libraries when using import libraries (most likely). So the libfftw3 dlls must be found at runtime.

However, simply putting the FFTW dll into the same .py directory was not sufficient. I needed to load it explicitly:

FFTWFile    = 'D:\\FFTW64\\libfftw3-3.dll'
FFTW64      = cdll.LoadLibrary(FFTWFile)

Upvotes: 0

Related Questions