我正在使用Python 3.7,正在使用CFFI构建Python扩展模块,该模块封装了另一个使用V140平台工具集构建的DLL。我将.pyd
文件和.dll
与python wheel捆绑在一起,Windows 10上的用户可以毫无问题地使用它。
但是,当尝试在全新安装了Python 3.7的干净Windows 7计算机上使用它时,出现ImportError: Dll load failed
错误。我知道解决的方法是安装“ Visual Studio 2015的Visual C ++可再发行组件”。
我已经对此进行了测试,并且可以正常工作。我对此有一些疑问:
从Python 3.5开始,vcruntime140.dll
与Python一起打包。为什么是这样?为什么不仅仅依靠系统之一?我发现史蒂夫·道尔(Steve Dower)的博客post最接近回答这个问题。
他说:
由于DLL加载的工作方式,如果尚未加载该版本的DLL,则将使用与扩展模块相邻的那个版本。如果已加载,将使用当前内存中的那个。
实际上,我们只需要包含一次即可。诀窍是确保先加载它。最终,唯一可靠的方法是将其包含在所有地方。
在此阶段,Python 3.5始终将包含vcruntime140.dll(尽管有时它可能会从顶级目录移动到DLL中),所以使用MSVC 14.0构建的扩展将始终具有它。
Python的安装程序installation notes建议在安装过程中可能需要更新系统的C运行时。但是,在上述干净的Windows 7计算机上,这似乎没有发生,并且系统文件夹(vcruntime140.dll
中没有C:\windows\SysWOW64
。那么这是什么意思呢?它有什么作用?我本以为它会像静默安装上述可再发行组件那样做。
您无需成为管理员(除非需要对C运行时库进行系统更新,或者为所有用户安装适用于Windows的Python启动器)
vcruntime140.dll
)的指导是什么?我希望我根本不必关心,而依靠Python安装程序来解决这个问题。我也知道在Windows 10上默认安装vcruntime140.dll
,这是Windows 7上的建议Windows更新。或者,为了获得更多上下文,我实际上是在创建一个安装程序,其中捆绑了一些脚本,这些脚本是我创建的库以及来自Python.org的Python安装程序。如果是Windows 7,我真的还需要捆绑并以静默方式安装Visual C ++可再发行组件吗?vcruntime140.dll
依赖关系放入我的系统文件夹中。但是,当我使用dumpbin /dependencies
检查依赖项时,它只是按名称提及它们。 我尝试在x86和x86-64 Windows 7安装上安装Python 3.7.3。我没有选择为所有用户安装Python,而是选择将其安装在本地用户目录中。我可以看到C运行时依赖项DLL实际上保存在此目录中。 现在,在将扩展模块创建并安装到虚拟环境中之后,仅当PATH中存在Python安装目录时,我才可以使用扩展模块而不会出现问题。在Windows DLL search order中,似乎在以下路径上找到了运行时DLL:
PATH环境变量中列出的目录。请注意,这不包括“应用程序路径”注册表项指定的每个应用程序路径。计算DLL搜索路径时不使用“应用路径”键。
我仍然不明白的是为什么为什么不使用已经加载的运行时?
如果我打开进程浏览器(来自systinternals)并从虚拟环境中打开python解释器会话,则可以看到C运行时已经加载。根据微软的说法:(重点是我的)
在系统搜索DLL之前,它会检查以下内容:
如果已在内存中加载了具有相同模块名称的DLL,则系统将使用已加载的DLL,无论它位于哪个目录中。系统都不会搜索该DLL。 >
如果该DLL在运行该应用程序的Windows版本的已知DLL列表中,则系统使用其已知DLL的副本(以及已知DLL的从属DLL,如果有的话)。系统不搜索DLL。有关当前系统上已知DLL的列表,请参见以下注册表项:HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ KnownDLLs。