Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't import dll module in Python

I've been stressin for a few days trying to compile a modified version of libuvc on windows and now that I've finally done it, I can't seem to load it on Python. This lib that I've already compiled and successfully imported using the same version of Python on Linux machines, doesn't like w10 at all.

System

  • win 10 64 bit
  • python 3.8 64 bit
  • libusb 1.022
  • libuvc.dll compiled with MinGW64

Problem

When trying the

import ctypes
import ctypes.util
name = ctypes.util.find_library('libuvc')
lib = ctypes.cdll.LoadLibrary(name)

I get the following error:

Could not find module 'C:\Program Files (x86)\libuvc\lib\libuvc.dll'.
Try using the full path with constructor syntax. 
Error: could not find libuvc!

The issue is that the file exists since it was found by util.find_library, but python doesn't think it is where it is, or maybe the output is just the default. What am I missing here? What could be failing to be unable to not just load the module, but find it? I'm sorry I don't have more output than this.

P.S: I've tried reformatting the string in different ways, but the message doesn't change.

like image 929
tigonza Avatar asked Dec 13 '19 23:12

tigonza


4 Answers

Starting with Python 3.8, the .dll search mechanism has changed (Win specific).

According to [Python.Docs]: os.add_dll_directory(path) (emphasis is mine):

Add a path to the DLL search path.

This search path is used when resolving dependencies for imported extension modules (the module itself is resolved through sys.path), and also by ctypes.

...

Availability: Windows.

So, you could do:

os.add_dll_directory("${path_to_working_dlls_directoy}")

where ${path_to_working_dlls_directoy} is a placeholder for the actual path and it (obviously) should be replaced by it.

You can check [SO]: PyWin32 and Python 3.8.0 (@CristiFati's answer) (which although it seems very different, has the same cause), for more details.

P.S.: Nix OSes are not impacted.

like image 170
CristiFati Avatar answered Oct 20 '22 20:10

CristiFati


A year late, but I've figured out what is going on and how to fix it. If you look at the code for ctypes.CDLL on around line 340, you can see that the docs are actually incorrect. The code defines the constructor as

def __init__(self, name, mode=DEFAULT_MODE, handle=None,
             use_errno=False, use_last_error=False, winmode=None):

The docs, however, say winmode=0. If you look at line 358, you can see that it matters quite a bit. When winmode=None, the search mode used by _ctypes.LoadLibrary in line 374 (aliased as _dlopen in line 110) is set to nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS on line 363. This search mode does not appear to respond to changes to os.environ['PATH'], sys.path or os.add_dll_directory.

However, if you bypass that setting by using winmode=0 instead of None, the library appears to load fine. Zero is a valid mode for a full path (as would be nt._LOAD_WITH_ALTERED_SEARCH_PATH). A full list of modes is available here: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa, under the dwFlags parameter.

As @CristiFati indicates, the behavior changed in Python 3.8. This happened because prior to that, the winmode parameter did not exist. Instead, mode was used directly, with a default value of ctypes.DEFAULT_MODE, which happens to correspond to a zero and work on all platforms.

Python bug report to resolve discrepancy: https://bugs.python.org/issue42114

like image 30
Mad Physicist Avatar answered Oct 20 '22 22:10

Mad Physicist


OK so i fixed it, it was required that i changed the working directory to where the script was being executed before loading the dll from the same place.

os.chdir('path_to_working_dlls_directoy')

not entirely sure why this helped though.

like image 32
tigonza Avatar answered Oct 20 '22 21:10

tigonza


I partially agree with @MadPhysicist's answer, but I have Python 3.9, not 3.8, and with winmode=0 error haven't disappeared. But with winmode=1 everything is working!

like image 1
Владимир Ивонин Avatar answered Oct 20 '22 21:10

Владимир Ивонин