通过清单通过 DLL 重定向加载两次 DLL 文件
我将 python.h
包含在我的 Visual C++ DLL 文件项目,导致与 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 应通过其搜索顺序找到的那个。
Process Explorer 的屏幕转储 http://dl.dropbox.com/u/3545118/python25_dll.png
为什么会发生这种情况?我怎样才能加载清单指向的 DLL 文件?
I'm including python.h
in my Visual C++ DLL file project which causes an implicit linking with python25.dll
. However, I want to load a specific python25.dll
(several can be present on the computer), so I created a very simple manifest file named 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>
And I'm merging it with the automatically embedded manifest file generated by Visual Studio thanks to:
Configuration Properties -> Manifest Tool -> Input and Output -> Additional Manifest Files
-->$(ProjectDir)\src\test.manifest
python25.dll
is now loaded twice: the one requested by the manifest, and the one that Windows should find through its search order.
Screendump of Process Explorer http://dl.dropbox.com/u/3545118/python25_dll.png
Why is that happening and how can I just load the DLL file pointed by the manifest?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
经过与 WinSxS 和 DLL 重定向的详尽战斗后,这是我的给您的建议:
一些背景知识
各种情况都可能导致 DLL 文件在 Windows 下加载:
LoadLibrary
) - 加载程序使用正在运行的 EXE 文件的当前激活上下文。这是直观的。A.exe
依赖于B.dll
依赖于C.dll
(全部隐式链接),则加载程序将使用B。加载
的激活上下文。 IIRC,这意味着如果 B 的C.dll
时 dllDllMain
加载C.dll
,它可以使用B.dll
的激活上下文 - 大多数时候它意味着系统范围的默认激活上下文。因此,您可以从%SystemRoot%
获取 Python DLL。CoCreateInstance
)——这是最讨厌的一个。极其微妙。事实证明,加载程序可以使用 COM(在HKCR\CLSID
下)从注册表中查找 DLL 文件的完整路径。如果用户提供完整路径,LoadLibrary
将不会执行任何搜索,因此激活上下文不会影响 DLL 文件解析。这些可以使用comClass
元素和其他元素进行重定向,请参阅[参考][msdn_ assembly_ref]。bp kernel32!ActivateActCtx
。现在开始寻找罪魁祸首
python25.dll
”或“详细信息包含python25.dll
”(对于 COM查找)。双击某个条目实际上会显示堆栈跟踪(您需要先设置符号搜索路径,并设置 Microsoft 的 PDB 服务器)。这应该足以满足您的大多数需求。!findstack MyExeModuleName
或~*k
code>) 导致加载 DLL 文件。现实世界的解决方案
不要摆弄这个 WinSxS 的东西,而是尝试使用 Mhook 挂钩
LoadLibraryW
或EasyHook。您可以完全用您的自定义逻辑替换该调用。你可以在午饭前完成这个,重新找到生命的意义。[msdn_ assembly_ref]:程序集清单
After exhaustive battle with WinSxS and DLL redirection, here's my advice for you:
Some background
Various things can cause a DLL file to be loaded under Windows:
LoadLibrary
) -- the loader uses the current activation context of the running EXE file. This is intuitive.A.exe
depends onB.dll
depends onC.dll
(all implicit linkage), the loader will useB.dll
's activation context when loadingC.dll
. IIRC, it means if B'sDllMain
loadsC.dll
, it can be usingB.dll
's activation context -- most of the time it means the system-wide default activation context. So you get your Python DLL from%SystemRoot%
.CoCreateInstance
) -- this is the nasty one. Extremely subtle. It turns out the loader can look up the full path of a DLL file from the registry using COM (underHKCR\CLSID
).LoadLibrary
will not do any searching if the user gives it a full path, so the activation context can't affect the DLL file resolution. Those can be redirected with thecomClass
element and friends, see [reference][msdn_assembly_ref].bp kernel32!ActivateActCtx
.Now on to finding the culprit
python25.dll
" or "Detail containingpython25.dll
" (for COM lookups). Double clicking an entry will actually show you a stack trace (you need to set the symbol search paths first, and also set Microsoft's PDB server). This should be enough for most of your needs.sxe ld python25
and look at what other threads are doing (!findstack MyExeModuleName
or~*k
) that causes a DLL file to load.Real world solution
Instead of fiddling with this WinSxS thing, try hooking
LoadLibraryW
using Mhook or EasyHook. You can just totally replace that call with your custom logic. You can finish this before lunch and find the meaning of life again.[msdn_assembly_ref]: Assembly Manifests
我对这个问题的理解取得了一些进展。
首先让我澄清一下场景:
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
...I made some progress for the understanding of the issue.
First let me clarify the scenario:
python25.dll
in the same folder as my DLL file, as well as aboost_python-vc90-mt-1_39.dll
.Then, when running the EXE file, the current directory is not the one containing
python25.dll
, and that's why the search order is used and some otherpython25.dll
can be found before mine.Now I figured out that the manifest technique was the good approach: I managed to redirect the loading to "my"
python25.dll
.The problem is that this is the Boost DLL file
boost_python-vc90-mt-1_39.dll
that's responsible for the "double" loading!If I don't load this one, then
python25.dll
is correctly redirected. Now I somehow have to figure out how to tell the Boost DLL file not to load anotherpython25.dll
...Dependency Walker 通常是解决此类问题的最佳工具。我不太确定它处理清单的效果如何......
在这个混乱的混乱中,实际的进程可执行文件在哪里?
我想到了两种可能性:
您正在编写一个 Python 扩展 DLL 文件。因此,Python 进程正在加载您的 DLL 文件,并且它已经拥有自己的 python25.dll 依赖项。
加载 DLL 文件的 EXE 文件是使用 DLL 文件项目提供的头文件和库构建的。因此它从头文件继承#pragma comment(lib,"python25.lib")并因此加载DLL文件本身。
第二种情况的问题是,我希望 EXE 文件和 DLL 文件位于同一文件夹中,以防 EXE 文件隐式加载 DLL 文件。在这种情况下,EXE 文件、DLL 文件和 python25.dll 都已位于同一文件夹中。那么为什么会加载 system32 版本呢?隐式加载的 DLL 文件的搜索顺序始终位于应用程序 EXE 文件的文件夹中。
因此,您的查询中隐含的实际有趣的问题是:system32 python26.dll 是如何加载的?
Dependency Walker is usually the best tool for resolving this kind of problem. I'm not too sure how well it handles manifests though...
Where in this entangled mess is the actual process executable file?
Two possibilities come to mind:
You are writing a Python extension DLL file. So the Python process is loading your DLL file, and it would already have its own python25.dll dependency.
The EXE file loading your DLL file is being built with header files and libraries provided by the DLL file project. So it is inheriting the
#pragma comment(lib,"python25.lib")
from your header file and as a result is loading the DLL file itself.My problem with the second scenario is, I'd expect the EXE file, and your DLL file, to be in the same folder in the case that the EXE file is implicitly loading your DLL file. In which case the EXE file, your DLL file and the python25.dll are all already in the same folder. Why then would the system32 version ever be loaded? The search order for implicitly loaded DLL files is always in the application EXE file's folder.
So the actual interesting question implicit in your query is: How is the system32 python26.dll being loaded at all?
最近,我遇到了一个非常类似的问题
导入 tkinter
导致第二次加载完全相同的内容python32.dll,但位于不同的非默认地址。_PyThreadState_Current == NULL
)。显然,从重复的 python32.dll 加载的第二个 Python 解释器从未调用过 Py_Initialize()。为什么 python32.dll 被加载两次?正如我在 我关于 python-capi 的帖子中解释的那样,这是由于应用程序从 WinSxS 加载 python32.dll,但 _tkinter.pyd 无法识别该程序集,因此使用常规 DLL 搜索路径加载 python32.dll。
Python.manifest + python32.dll 程序集被 DLL 加载机制识别为与 _tkinter.pyd 请求的 python32.dll 不同的模块(在不同的激活上下文下)。
从嵌入 Python 的应用程序中删除对 Python.manifest 的引用并允许 DLL 搜索路径查找 DLL 解决了该问题。
Recently, I hit a very similar problem:
import tkinter
inside the embedded Python interpreter caused second loading of the very same python32.dll, but under a different non-default address._PyThreadState_Current == NULL
). Obviously,Py_Initialize()
was never called for the second Python interpreter loaded from the duplicate python32.dll.Why was the python32.dll loaded twice? As I explained in my post on python-capi, this was caused by the fact the application was loading python32.dll from WinSxS, but _tkinter.pyd did not recognise the assembly, so python32.dll was loaded using the regular DLL search path.
The Python.manifest + python32.dll assembly was recognised by the DLL loading machinery as a different module (under different activation context), than the python32.dll requested by _tkinter.pyd.
Removing the reference to Python.manifest from the application embedding Python and allowing the DLL search path to look for the DLLs solved the problem.