在ctypes中查找库()

2024-06-01 05:14:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图使用ctypes中的find_library()命令,但是我得到了一个错误,我不明白它的原因。我在窗户上工作

这是代码:

import ctypes
from ctypes.util import find_library
import numpy
from string import atoi
from time import sleep

# Class constants
#nidaq = ctypes.windll.nicaiu
nidaq  = ctypes.cdll.LoadLibrary(find_library('NIDAQmx'))

这就是我得到的错误:

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    nidaq  = ctypes.cdll.LoadLibrary(find_library('NIDAQmx'))
  File "C:\Python27\lib\ctypes\__init__.py", line 443, in LoadLibrary
    return self._dlltype(name)
  File "C:\Python27\lib\ctypes\__init__.py", line 365, in __init__
    self._handle = _dlopen(self._name, mode)
TypeError: expected string or Unicode object, NoneType found

我是否应该将NIDAQmx放在一个特定的位置,例如,以便可以找到它?或者这是无关的?

谢谢!


Tags: infromimportselfstringinit错误line
2条回答

您要搜索的库位于计算机上的公用位置。find_library不会对文件系统执行任意搜索,它会在列出ctypes/macholib/dyld.py模块的特定位置查找(请参阅dyld_find函数)。

如果库位于例如/usr/lib的位置,则应该找到它,但如果它位于非标准位置,则必须将其目录添加到诸如DYLD_LIBRARY_PATH的环境变量中。

在Windows上,find_library搜索PATH环境变量中的目录,这不是Windows加载程序使用的真正的search order for desktop applications。值得注意的是find_library不包括应用程序目录和当前目录。

调用Windows SearchPath会更接近,但给定DLL activation contexts和其他api(如SetDllDirectory)或较新的api SetDefaultDllDirectoriesAddDllDirectory)则不会更接近。

如果没有简单的方法复制Windows加载程序使用的搜索,只需使用CDLL(cdecl)或WinDLL(stdcall)按名称加载DLL:

nidaq_cdecl   = ctypes.CDLL('NIDAQmx')
nidaq_stdcall = ctypes.WinDLL('NIDAQmx')

可以在运行时动态地将DLL目录添加到PATH(与启动时Linux加载程序缓存LD_LIBRARY_PATH不同)。例如,假设您的DLL依赖项位于包的“dlls”子目录中。可以按如下方式预先设置此目录:

import os

basepath = os.path.dirname(os.path.abspath(__file__))
dllspath = os.path.join(basepath, 'dlls')
os.environ['PATH'] = dllspath + os.pathsep + os.environ['PATH']

或者,可以使用调用^{}^{}的上下文管理器临时修改当前工作目录通常占用的搜索槽。记住,就像修改PATH一样,这会修改全局进程数据,因此在使用多个线程时应小心。这种方法的一个优点是它不修改CreateProcess用于查找可执行文件的搜索路径。

import os
import ctypes
from ctypes import wintypes
from contextlib import contextmanager

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

def check_dword(result, func, args):
    if result == 0:
        last_error = ctypes.get_last_error()
        if last_error != 0:
            raise ctypes.WinError(last_error)
    return args

def check_bool(result, func, args):
    if not result:
        last_error = ctypes.get_last_error()
        if last_error != 0:
            raise ctypes.WinError(last_error)
        else:
            raise OSError
    return args

kernel32.GetDllDirectoryW.errcheck = check_dword
kernel32.GetDllDirectoryW.argtypes = (wintypes.DWORD,  # _In_  nBufferLength
                                      wintypes.LPWSTR) # _Out_ lpBuffer

kernel32.SetDllDirectoryW.errcheck = check_bool
kernel32.SetDllDirectoryW.argtypes = (wintypes.LPCWSTR,) # _In_opt_ lpPathName

@contextmanager
def use_dll_dir(dll_dir):
    size = newsize = 0
    while newsize >= size:
        size = newsize
        prev = (ctypes.c_wchar * size)()
        newsize = kernel32.GetDllDirectoryW(size, prev)
    kernel32.SetDllDirectoryW(os.path.abspath(dll_dir))
    try:
        yield
    finally:
        kernel32.SetDllDirectoryW(prev)

例如:

if __name__ == '__main__':
    basepath = os.path.dirname(os.path.abspath(__file__))
    dllspath = os.path.join(basepath, 'dlls')
    with use_dll_dir(dllspath):
        nidaq = ctypes.CDLL('NIDAQmx')

当然,如果您只想在启动时设置一次DLL目录,问题会简单得多。直接调用SetDllDirectoryW


另一种方法是使用标记LOAD_WITH_ALTERED_SEARCH_PATH调用LoadLibraryEx,这会临时将加载的DLL目录添加到搜索路径中。需要使用绝对路径加载DLL,否则行为未定义。为了方便起见,我们可以子类ctypes.CDLLctypes.WinDLL来调用LoadLibraryEx,而不是LoadLibrary

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

def check_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

kernel32.LoadLibraryExW.errcheck = check_bool
kernel32.LoadLibraryExW.restype = wintypes.HMODULE
kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR,
                                    wintypes.HANDLE,
                                    wintypes.DWORD)

class CDLLEx(ctypes.CDLL):
    def __init__(self, name, mode=0, handle=None, 
                 use_errno=True, use_last_error=False):
        if handle is None:
            handle = kernel32.LoadLibraryExW(name, None, mode)
        super(CDLLEx, self).__init__(name, mode, handle,
                                     use_errno, use_last_error)

class WinDLLEx(ctypes.WinDLL):
    def __init__(self, name, mode=0, handle=None, 
                 use_errno=False, use_last_error=True):
        if handle is None:
            handle = kernel32.LoadLibraryExW(name, None, mode)
        super(WinDLLEx, self).__init__(name, mode, handle,
                                       use_errno, use_last_error)

以下是所有可用的LoadLibraryEx标志:

DONT_RESOLVE_DLL_REFERENCES         = 0x00000001
LOAD_LIBRARY_AS_DATAFILE            = 0x00000002
LOAD_WITH_ALTERED_SEARCH_PATH       = 0x00000008
LOAD_IGNORE_CODE_AUTHZ_LEVEL        = 0x00000010  # NT 6.1
LOAD_LIBRARY_AS_IMAGE_RESOURCE      = 0x00000020  # NT 6.0
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE  = 0x00000040  # NT 6.0

# These cannot be combined with LOAD_WITH_ALTERED_SEARCH_PATH.
# Install update KB2533623 for NT 6.0 & 6.1.
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR    = 0x00000100
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
LOAD_LIBRARY_SEARCH_USER_DIRS       = 0x00000400
LOAD_LIBRARY_SEARCH_SYSTEM32        = 0x00000800
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS    = 0x00001000

例如:

if __name__ == '__main__':
    basepath = os.path.dirname(os.path.abspath(__file__))
    dllpath = os.path.join(basepath, 'dlls', 'NIDAQmx.dll')
    nidaq = CDLLEx(dllpath, LOAD_WITH_ALTERED_SEARCH_PATH)

相关问题 更多 >