ctypes中的find_library() [英] find_library() in ctypes
问题描述
我正在尝试使用ctypes中的命令find_library(),但出现一个错误,原因是我不明白其原因。我正在Windows上工作
这是代码:
import ctypes
from ctypes.util import find_library
import numpy
from string import atoi
from time import sleep
#类常量
#nidaq = ctypes.windll.nicaiu
nidaq = ctypes.cdll.LoadLibrary(find_library('NIDAQmx'))
这是我得到的错误:
跟踪(最近一次通话最近):
在< module>中,文件< pyshell#4>,第1行,
nidaq = ctypes.cdll.LoadLibrary(find_library('NIDAQmx'))
文件 C:\Python27\lib\ctypes\__init __。py,行443,在LoadLibrary $ b中$ b return self._dlltype(name)
文件 C:\Python27\lib\ctypes\__init __。py,第365行,位于__init__
self._handle = _dlopen(self。 _name,mode)
TypeError:预期的字符串或Unicode对象,NoneType找到
我应该放置例如,在特定位置可以找到NIDAQmx?还是这无关?
谢谢!
在Windows上, find_library
搜索 PATH
环境变量中的目录,该目录不是真正的 Windows加载程序所使用的桌面应用程序搜索顺序。值得注意的是, find_library
不包括应用程序目录和当前目录。
调用Windows SearchPath
会更近一些,但在DLL 激活上下文和其他API,例如 SetDllDirectory
或更新的API SetDefaultDllDirectories
和 AddDllDirectory
。
鉴于没有简单的方法来复制Windows加载程序使用的搜索,只需使用 CDLL $ c按名称加载DLL $ c>(cdecl)或
WinDLL
(stdcall):
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( )
如果last_error!= 0:
提高ctypes.WinError(last_error)
返回args
def check_bool(result,func,args):
如果没有结果:
last_error = ctypes.get_last_error()
如果last_error!= 0:
抛出ctypes.WinError(last_error)
否则:
引发OSError
return args
kernel32.GetDllDirectoryW.errcheck = check_dword
kernel32.GetDllDirectoryW.argtypes =(wintypes.DWORD,#_In _ _ nBufferLength
wintypes。 b
@contextmanager
def use_dll_dir(dll_dir):
size = newsize = 0
而newsize> =大小:
size = newsize
prev = (ctypes.c_wchar * size)()
newsize = kernel32.GetDllDirectoryW(size,prev)
kernel32.SetDllDirectoryW(os.path.abspath(dll_dir))
试试:
最后产生
:
kernel32.SetDllDirectoryW(prev)
例如:
如果__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
。
另一种方法是调用 LoadLibraryEx
,带有标志 LOAD_WITH_ALTERED_SEARCH_PATH
,该标志可将加载的DLL目录临时添加到搜索路径中。您需要使用绝对路径加载DLL,否则行为是不确定的。为了方便起见,我们可以将 ctypes.CDLL
和 ctypes.WinDLL
子类化,以调用 LoadLibraryEx
而不是 LoadLibrary
。
从ctypes中导入ctypes
import wintypes
kernel32 = ctypes.WinDLL('kernel32',use_last_error = True)
def check_bool(结果,函数,args ):
如果没有结果:
提高ctypes.WinError(ctypes.get_last_error())
返回args
kernel32.LoadLibraryExW.errcheck = check_bool
kernel32.LoadLibraryExW.restype = wintypes.HMODULE
kernel32.LoadLibraryExW.argtypes =(wintypes.LPCWSTR,
wintypes.HANDLE,
wintypes.DWORD)
类CDLLEx (ctypes.CDLL):
def __init __((自身,名称,模式= 0,句柄=无,
use_errno = True,use_last_error = False):
如果句柄为无:
手柄= kerne l32.LoadLibraryExW(名称,无,模式)
super(CDLLEx,self).__ init __(名称,模式,句柄,
use_errno,use_last_error)
类WinDLLEx(ctypes。 WinDLL):
def __init __(自身,名称,模式= 0,句柄=无,
use_errno = False,use_last_error = True):
如果handle为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_EL_V_10 = 6.1 LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x0000002 0#NT 6.0
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040#NT 6.0
#这些不能与LOAD_WITH_ALTERED_SEARCH_PATH结合使用。
#为NT 6.0和更高版本安装更新KB2533623。 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
例如:
如果__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)
I am trying to use the command find_library() from ctypes but I'm getting an error that I don't understand its reason. I am working on Windows
This is the code:
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'))
And this is the error I'm getting:
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
Should I place NIDAQmx in a specific place for example so that it can be found? Or this is unrelated?
Thanks!
解决方案 On Windows, find_library
searches the directories in the PATH
environment variable, which isn't the real search order for desktop applications that's used by the Windows loader. Notably find_library
doesn't include the application directory and the current directory.
Calling Windows SearchPath
would be closer, but not much closer given DLL activation contexts and other APIs such as SetDllDirectory
or the newer APIs SetDefaultDllDirectories
and AddDllDirectory
.
Given there's no simple way to replicate the search that's used by the Windows loader, just load the DLL by name using either CDLL
(cdecl) or WinDLL
(stdcall):
nidaq_cdecl = ctypes.CDLL('NIDAQmx')
nidaq_stdcall = ctypes.WinDLL('NIDAQmx')
You can add the DLL directory to PATH
dynamically at runtime (in contrast to the Linux loader's caching of LD_LIBRARY_PATH
at startup). For example, say your DLL dependencies are in the "dlls" subdirectory of your package. You can prepend this directory as follows:
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']
Alternatively, you can use a context manager that calls GetDllDirectory
and SetDllDirectory
to temporarily modify the search slot that's normally occupied by the current working directory. Bear in mind that, just like modifying PATH
, this modifies global process data, so care should be taken when using multiple threads. An advantage to this approach is that it doesn't modify the search path that CreateProcess
uses to find executables.
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)
For example:
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')
Of course, if you're only interested in setting the DLL directory once at startup the problem is much simpler. Just call SetDllDirectoryW
directly.
Another approach is to call LoadLibraryEx
with the flag LOAD_WITH_ALTERED_SEARCH_PATH
, which temporarily adds the loaded DLL directory to the search path. You need to load the DLL using an absolute path, else the behavior is undefined. For convenience we can subclass ctypes.CDLL
and ctypes.WinDLL
to call LoadLibraryEx
instead of 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)
Here are all of the available LoadLibraryEx
flags:
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
For example:
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)
这篇关于ctypes中的find_library()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!