gpt4 book ai didi

python - ctypes 中的 find_library()

转载 作者:太空狗 更新时间:2023-10-30 01:54:31 25 4
gpt4 key购买 nike

我正在尝试使用来自 ctypes 的命令 find_library() 但我收到一个错误,我不明白它的原因。我在 Windows 上工作

这是代码:

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 放在特定位置以便可以找到它?或者这是无关的?

谢谢!

最佳答案

在 Windows 上,find_library 搜索 PATH 环境变量中的目录,这不是真正的 search order for desktop applications这是由 Windows 加载程序使用的。值得注意的是 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']

或者,您可以使用调用 GetDllDirectory 的上下文管理器和 SetDllDirectory临时修改通常由当前工作目录占用的搜索槽。请记住,就像修改 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)

关于python - ctypes 中的 find_library(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23804438/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com