如何创建加载器实例`cdll`的属性?

时间:2018-06-13 13:35:10

标签: python python-3.x shared-libraries ctypes dynamic-loading

来自https://docs.python.org/3/library/ctypes.html#loading-shared-libraries

  

也可以使用预制的其中一个来加载共享库   对象,它们是LibraryLoader类的实例, by   通过检索库来调用LoadLibrary()方法   加载程序实例的属性

我找到了第一种方式的例子Free the opened ctypes library in Python

我想知道如何使用第二种方式?特别是,如何创建加载器实例cdll的属性?我的问题来自Why does loading the libc shared library have "'LibraryLoader' object is not callable" error?

  

LibraryLoader的重点在于,当您访问它时,它会为您创建库。而cdll.LoadLibrary("foo")并未创建cdll.foo

我的实验有问题吗?为什么cdll.libc永远不存在?

>>> from ctypes import *
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1afac8>
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> libc=cdll.LoadLibrary("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> CDLL("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1af978>
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory

>>> libc=CDLL("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.__dict__
{'_dlltype': <class 'ctypes.CDLL'>}

1 个答案:

答案 0 :(得分:1)

示例(发生了什么):

>>> import sys
>>> import ctypes
>>> print("Python {:s} on {:s}".format(sys.version, sys.platform))
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
>>>
>>> [item for item in dir(ctypes.windll) if "__" not in item]
['LoadLibrary', '_dlltype', 'kernel32']
>>>
>>> user32_dll = ctypes.windll.LoadLibrary("user32")
>>> user32_dll
<WinDLL 'user32', handle 7ff882810000 at 0x2434399b4e0>
>>> [item for item in dir(ctypes.windll) if "__" not in item]
['LoadLibrary', '_dlltype', 'kernel32']
>>>
>>> user32_dll = ctypes.WinDLL("user32")
>>> user32_dll
<WinDLL 'user32', handle 7ff882810000 at 0x2434399b4a8>
>>> [item for item in dir(ctypes.windll) if "__" not in item]
['LoadLibrary', '_dlltype', 'kernel32']
>>>
>>> user32_dll = ctypes.windll.user32
>>> user32_dll
<WinDLL 'user32', handle 7ff882810000 at 0x24343984d68>
>>> [item for item in dir(ctypes.windll) if "__" not in item]
['LoadLibrary', '_dlltype', 'kernel32', 'user32']
>>>
>>> ctypes.windll.user32
<WinDLL 'user32', handle 7ff882810000 at 0x24343984d68>
>>>
>>> ctypes.windll.user321
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\install\x64\python\python\3.5\Lib\ctypes\__init__.py", line 421, in __getattr__
    dll = self._dlltype(name)
  File "c:\install\x64\python\python\3.5\Lib\ctypes\__init__.py", line 351, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] The specified module could not be found
>>>
>>> dir(ctypes.windll)
['LoadLibrary', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_dlltype', 'kernel32', 'user32']

如何它发生 - 它全部在“$ {PYTHON_SRC_DIR} / Lib / ctypes / __ init __。py”
[GitHub]: python/cpython - (master) cpython/Lib/ctypes/__init__.py)。我正在粘贴cdll的代码,因为windll(我在上面的示例中使用的)只是它的包装(并且需要更多的代码):

# ...

class CDLL(object):

# ...

class LibraryLoader(object):
    def __init__(self, dlltype):
        self._dlltype = dlltype

    def __getattr__(self, name):
        if name[0] == '_':
            raise AttributeError(name)
        dll = self._dlltype(name)
        setattr(self, name, dll)  # @TODO - cfati: This is the key for always returning the same instance.
        return dll

# ...

cdll = LibraryLoader(CDLL)

# ...

<强> @ EDIT0

嗯,在 Ux (至少 Lnx )上,事情并不是那么好看:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> ls
libcapi.so
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) python3 -c "import ctypes; ctypes.cdll.LoadLibrary('libcapi.so')"
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) python3 -c "import ctypes; ctypes.cdll.LoadLibrary('libcapi')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
    return self._dlltype(name)
  File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libcapi: cannot open shared object file: No such file or directory

这是因为显然(与[MSDN]: LoadLibrary function相反),[man7]: DLOPEN(3)不会将(默认)库扩展名( .so )附加到文件名(如果它不包含它。)

code.c

#include <stdio.h>
#include <dlfcn.h>


int main(int argc, char *argv[]) {
    if (argc == 1) {
        printf("Dll name required\n");
        return 1;
    }
    void *handle = dlopen(argv[1], RTLD_NOW);
    if (handle == NULL) {
        printf("Could not load [%s]\n", argv[1]);
        return 2;
    } else {
        printf("Successfully loaded [%s]\n", argv[1]);
        dlclose(handle);
        return 0;
    }
}

<强>输出

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> gcc code.c -Wl,-ldl
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> ./a.out "libcapi.so"
Could not load [libcapi.so]
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) ./a.out "libcapi.so"
Successfully loaded [libcapi.so]
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) ./a.out "libcapi"
Could not load [libcapi]

因此,无法在 Ux 上复制 Win 行为(遗憾的是,. dot )无法复制成为属性名称的一部分,以克服这一点)。或者,链接器是否可以配置为隐式搜索 .so 文件?但这只会部分解决问题,因为许多库看起来像 libc.so.6 (或 AFAIK OSX 。所以 .dylib 确定)。