How can I unload a DLL using ctypes in Python?

前端 未结 5 1457
一整个雨季
一整个雨季 2020-11-29 07:50

I\'m using ctypes to load a DLL in Python. This works great.

Now we\'d like to be able to reload that DLL at runtime.

The straightforward approach would s

5条回答
  •  独厮守ぢ
    2020-11-29 07:54

    windows and linux compatible minimal reproducible example from 2020

    overview of similar discussion

    Here an overview of similar discussions (where I constructed this answer from).

    • How can I unload a DLL using ctypes in Python?
    • ctypes unload dll
    • Unload shared library inside ctypes loaded shared library
    • forcing ctypes.cdll.LoadLibrary() to reload library from file

    minimal reproducible example

    This is for windows and linux, hence there are 2 scripts given for compilation. Tested under:

    • Win 8.1, Python 3.8.3 (anaconda), ctypes 1.1.0, mingw-w64 x86_64-8.1.0-posix-seh-rt_v6-rev0
    • Linux Fedora 32, Python 3.7.6 (anaconda), ctypes 1.1.0, g++ 10.2.1

    cpp_code.cpp

    extern "C" int my_fct(int n)
    {
        int factor = 10;
        return factor * n;
    }
    

    compile-linux.sh

    #!/bin/bash
    g++ cpp_code.cpp -shared -o myso.so
    

    compile-windows.cmd

    set gpp="C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\g++.exe"
    %gpp% cpp_code.cpp -shared -o mydll.dll
    PAUSE
    

    Python code

    from sys import platform
    import ctypes
    
    
    if platform == "linux" or platform == "linux2":
        # https://stackoverflow.com/a/50986803/7128154
        # https://stackoverflow.com/a/52223168/7128154
    
        dlclose_func = ctypes.cdll.LoadLibrary('').dlclose
        dlclose_func.argtypes = [ctypes.c_void_p]
    
        fn_lib = './myso.so'
        ctypes_lib = ctypes.cdll.LoadLibrary(fn_lib)
        handle = ctypes_lib._handle
    
        valIn = 42
        valOut = ctypes_lib.my_fct(valIn)
        print(valIn, valOut)
    
        del ctypes_lib
        dlclose_func(handle)
    
    elif platform == "win32": # Windows
        # https://stackoverflow.com/a/13129176/7128154
        # https://stackoverflow.com/questions/359498/how-can-i-unload-a-dll-using-ctypes-in-python
    
        lib = ctypes.WinDLL('./mydll.dll')
        libHandle = lib._handle
    
        # do stuff with lib in the usual way
        valIn = 42
        valOut = lib.my_fct(valIn)
        print(valIn, valOut)
    
        del lib
        ctypes.windll.kernel32.FreeLibrary(libHandle)
    

    A more general solution (object oriented for shared libraries with dependencies)

    If a shared library has dependencies, this does not necessarily work anymore (but it can - depends on the dependency ^^). I did not investigate the very details, but it looks like the mechanism is the following: library and dependency are loaded. As the dependency is not unloaded, the library can not get unloaded.

    I found, that if I include OpenCv (Version 4.2) into my shared library, this messes up the unloading procedure. The following example was only tested on the linux system:

    code.cpp

    #include 
    #include  
    
    
    extern "C" int my_fct(int n)
    {
        cv::Mat1b mat = cv::Mat1b(10,8,(unsigned char) 1 );  // change 1 to test unloading
        
        return mat(0,1) * n;
    }
    

    Compile with g++ code.cpp -shared -fPIC -Wall -std=c++17 -I/usr/include/opencv4 -lopencv_core -o so_opencv.so

    Python code

    from sys import platform
    import ctypes
    
    
    class CtypesLib:
    
        def __init__(self, fp_lib, dependencies=[]):
            self._dependencies = [CtypesLib(fp_dep) for fp_dep in dependencies]
    
            if platform == "linux" or platform == "linux2":  # Linux
                self._dlclose_func = ctypes.cdll.LoadLibrary('').dlclose
                self._dlclose_func.argtypes = [ctypes.c_void_p]
                self._ctypes_lib = ctypes.cdll.LoadLibrary(fp_lib)
            elif platform == "win32":  # Windows
                self._ctypes_lib = ctypes.WinDLL(fp_lib)
    
            self._handle = self._ctypes_lib._handle
    
        def __getattr__(self, attr):
            return self._ctypes_lib.__getattr__(attr)
    
        def __del__(self):
            for dep in self._dependencies:
                del dep
    
            del self._ctypes_lib
    
            if platform == "linux" or platform == "linux2":  # Linux
                self._dlclose_func(self._handle)
            elif platform == "win32":  # Windows
                ctypes.windll.kernel32.FreeLibrary(self._handle)
    
    
    fp_lib = './so_opencv.so'
    
    ctypes_lib = CtypesLib(fp_lib, ['/usr/lib64/libopencv_core.so'])
    
    valIn = 1
    ctypes_lib.my_fct.argtypes = [ctypes.c_int]
    ctypes_lib.my_fct.restype = ctypes.c_int
    valOut = ctypes_lib.my_fct(valIn)
    print(valIn, valOut)
    
    del ctypes_lib
    

    Let me know, when there are any issues with the code examples or the explanation given so far. Also if you know a better way! It would be great, if we could settle the issue once and for all.

提交回复
热议问题