我在Visual C++ DLL文件项目中包含python.h
,导致与python25.dll
的隐式链接。但是,我想加载一个特定的python25.dll
(计算机上可以存在几个),所以我创建了一个名为 test.manifest 的非常简单的清单文件:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<file name="python25.dll" />
</assembly>
我正在将它与Visual Studio生成的自动嵌入式清单文件合并,这要归功于:
Configuration Properties -> Manifest Tool -> Input and Output -> Additional Manifest Files
-->$(ProjectDir)\src\test.manifest
python25.dll
现在加载了两次:清单请求的那个,以及Windows应该通过搜索顺序找到的那个。
Screendump of Process Explorer http://dl.dropbox.com/u/3545118/python25_dll.png
为什么会发生这种情况?如何加载清单指向的DLL文件?
答案 0 :(得分:6)
与WinSxS和DLL重定向进行彻底的争斗后,这是我对你的建议:
各种事情都可能导致在Windows下加载DLL文件:
LoadLibrary
) - 加载程序使用正在运行的EXE文件的当前激活上下文。这很直观。A.exe
取决于B.dll
,则取决于C.dll
(所有隐式链接),加载器将在加载B.dll
时使用C.dll
的激活上下文。 IIRC,这意味着如果B DllMain
加载C.dll
,它可以使用B.dll
的激活上下文 - 大多数时候它意味着系统范围的默认激活上下文。所以你从%SystemRoot%
获得了你的Python DLL。CoCreateInstance
) - 这是令人讨厌的。非常微妙。事实证明,加载程序可以使用COM(在HKCR\CLSID
下)从注册表中查找DLL文件的完整路径。如果用户给它一个完整路径,LoadLibrary
将不会进行任何搜索,因此激活上下文不会影响DLL文件解析。这些可以使用comClass
元素和朋友重定向,请参阅[reference] [msdn_assembly_ref]。bp kernel32!ActivateActCtx
。python25.dll
”或“详细信息包含python25.dll
”(用于COM查找)。双击条目实际上会显示堆栈跟踪(您需要先设置符号搜索路径,还要设置Microsoft的PDB服务器)。这应该足以满足您的大多数需求。sxe ld python25
并查看导致DLL文件加载的其他线程正在做什么(!findstack MyExeModuleName
或~*k
)。尝试使用Mhook或EasyHook挂钩LoadLibraryW
,而不是摆弄这个WinSxS。您可以使用自定义逻辑完全替换该调用。你可以在午餐前完成这项工作,再次找到生命的意义。
[msdn_assembly_ref]: Assembly Manifests
答案 1 :(得分:2)
我在理解这个问题方面取得了一些进展。
首先让我澄清一下情景:
python25.dll
,以及boost_python-vc90-mt-1_39.dll
。然后,在运行EXE文件时,当前目录不是包含python25.dll
的目录,这就是使用搜索顺序的原因,并且在我之前可以找到其他python25.dll
。
现在我发现清单技术是一种很好的方法:我设法将加载重定向到“我的”python25.dll
。
问题是这是Boost DLL文件boost_python-vc90-mt-1_39.dll
负责“双重”加载!
如果我没有加载这个,那么python25.dll
被正确重定向。现在我不得不弄清楚如何告诉Boost DLL文件不要加载另一个python25.dll
......
答案 2 :(得分:1)
Dependency Walker通常是解决此类问题的最佳工具。我不太确定它处理清单的效果如何......
在这个纠结的混乱中,实际的流程可执行文件是什么?
有两种可能性浮现在脑海中:
您正在编写Python扩展DLL文件。所以Python进程正在加载你的DLL文件, 已经有了自己的python25.dll依赖。
正在使用DLL文件项目提供的头文件和库构建加载DLL文件的EXE文件。所以它从头文件继承#pragma comment(lib,"python25.lib")
,结果就是加载DLL文件。
第二种情况的问题是,在EXE文件隐式加载DLL文件的情况下,我希望EXE文件和您的DLL文件位于同一文件夹中。在这种情况下,EXE文件,您的DLL文件和python25.dll都已存在于同一文件夹中。为什么然后会加载system32版本?隐式加载的DLL文件的搜索顺序始终位于应用程序EXE文件的文件夹中。
因此,查询中隐含的实际有趣问题是:system32 python26.dll是如何加载的?
答案 3 :(得分:0)
最近,我遇到了非常similar problem:
import tkinter
导致第二次加载相同的 python32.dll ,但在不同的非默认地址下。< / LI>
_PyThreadState_Current == NULL
)。显然,Py_Initialize()
从未被调用从复制的python32.dll加载的第二个Python解释器。为什么python32.dll加载了两次?正如我在my post on python-capi中解释的那样,这是因为应用程序从WinSxS加载python32.dll,但 _tkinter.pyd 无法识别程序集,因此使用python32.dll加载常规DLL搜索路径。
Python.manifest + python32.dll程序集被DLL加载机制识别为一个不同的模块(在不同的激活上下文下),而不是_tkinter.pyd请求的python32.dll。
从嵌入Python的应用程序中删除对Python.manifest的引用,并允许DLL搜索路径查找DLL解决了这个问题。