C++ DLL文件加载两次,并通过清单进行DLL重定向

C++ DLL文件加载两次,并通过清单进行DLL重定向,c++,python,visual-studio,dll,manifest,C++,Python,Visual Studio,Dll,Manifest,我将python.h包含在我的DLL文件项目中,这会导致与python25.DLL的隐式链接。但是,我想加载一个特定的python25.dll(计算机上可能有几个),因此我创建了一个非常简单的清单文件test.manifest: python25.dll现在被加载两次:清单请求的一次,以及Windows应该通过其搜索顺序找到的一次 为什么会发生这种情况?我如何加载清单所指向的DLL文件?通常是解决此类问题的最佳工具。不过我不太确定它处理清单的效果如何 在这种混乱中,实际的进程可执行文件在哪里

我将
python.h
包含在我的DLL文件项目中,这会导致与
python25.DLL
的隐式链接。但是,我想加载一个特定的
python25.dll
(计算机上可能有几个),因此我创建了一个非常简单的清单文件test.manifest:

python25.dll
现在被加载两次:清单请求的一次,以及Windows应该通过其搜索顺序找到的一次

为什么会发生这种情况?我如何加载清单所指向的DLL文件?

通常是解决此类问题的最佳工具。不过我不太确定它处理清单的效果如何

在这种混乱中,实际的进程可执行文件在哪里

我想到了两种可能性:

  • 您正在编写Python扩展DLL文件。因此Python进程正在加载您的DLL文件,并且它已经有了自己的python25.DLL依赖项

  • 正在使用DLL文件项目提供的头文件和库构建加载DLL文件的EXE文件。因此,它从头文件继承了
    #pragma注释(lib,“python25.lib”)
    ,因此加载DLL文件本身

  • 第二种方案的问题是,如果EXE文件隐式加载DLL文件,我希望EXE文件和DLL文件位于同一文件夹中。在这种情况下,EXE文件、DLL文件和python25.DLL都已位于同一文件夹中。那么为什么要加载system32版本呢?隐式加载的DLL文件的搜索顺序始终在应用程序EXE文件的文件夹中


    因此,查询中隐含的真正有趣的问题是:system32 python26.dll是如何加载的

    我在理解这个问题上取得了一些进展

    首先让我澄清一下情况:

    • 我正在使用Python C API和Boost.Python构建一个嵌入和扩展Python的DLL文件
    • 因此,我在dll文件所在的文件夹中提供了一个
      python25.dll
      ,以及一个
      boost\u python-vc90-mt-1\u 39.dll
    • 然后我有一个EXE文件,它是一个演示如何链接到我的DLL文件的示例:只要DLL文件可以在路径中找到,这个EXE文件就不必和我的DLL文件在同一个文件夹中(我假设最终用户可以也可以不把它放在同一个文件夹中)
    然后,当运行EXE文件时,当前目录不是包含
    python25.dll
    的目录,这就是为什么使用搜索顺序,并且可以在我之前找到其他一些
    python25.dll

    现在我发现清单技术是一种很好的方法:我设法将加载重定向到“my”
    python25.dll

    问题在于,这是负责“双重”加载的DLL文件
    boost_python-vc90-mt-1_39.DLL


    如果我不加载这个,那么
    python25.dll
    将被正确重定向。现在,我必须想办法告诉Boost DLL文件不要加载另一个
    python25.DLL
    ..

    在与DLL重定向进行了彻底的斗争之后,我给你的建议如下:

    一些背景 各种因素可能导致在Windows下加载DLL文件:

    • 显式链接(
      LoadLibrary
      )--加载程序使用正在运行的EXE文件的当前激活上下文。这是直观的
    • 隐式链接(“加载时间链接”,即“自动”链接)--加载程序使用取决于DLL文件的默认激活上下文。如果
      A.exe
      依赖于
      B.dll
      依赖于
      C.dll
      (所有隐式链接),加载程序将在加载
      C.dll
      时使用
      B.dll
      的激活上下文。IIRC,这意味着如果B的
      DllMain
      加载
      C.dll
      ,它可以使用
      B.dll
      的激活上下文——大多数情况下,它意味着系统范围的默认激活上下文。因此,您可以从
      %SystemRoot%
      获取Python DLL
    • COM(
      CoCreateInstance
      )--这是一个令人讨厌的例子。非常微妙。事实证明,加载程序可以使用COM(在
      HKCR\CLSID
      下)从注册表中查找DLL文件的完整路径<如果用户提供完整路径,则code>LoadLibrary不会执行任何搜索,因此激活上下文不会影响DLL文件的分辨率。可以使用
      comClass
      元素和好友重定向这些内容,请参阅[参考][msdn\u assembly\u ref]
    • 即使您拥有正确的清单,有时仍有人可以在运行时使用更改激活上下文。如果是这种情况,通常你对此无能为力(见下面的最终解决方案);这只是为了完整性。如果你想知道是谁在干扰激活上下文,WinDbg
      BPKernel32!激活eACTCTX
    现在开始寻找罪犯
  • 找出加载DLL文件的原因的最简单方法是使用。您可以查看“路径包含
    python25.dll
    ”或“详细信息包含
    python25.dll
    ”(用于COM查找)。双击条目实际上会显示堆栈跟踪(您需要先设置符号搜索路径,还需要设置Microsoft的PDB服务器)。这应该足以满足您的大部分需求
  • 有时,从上面获得的堆栈跟踪可以从新线程生成。为此,您需要。这可能是另一个主题,但只需说您可以
    sxe ld python25
    并查看其他线程正在执行哪些导致加载DLL文件的操作(
    !findstack MyExeModuleName
    ~*k
  • 真实世界解决方案 与其摆弄WinSxS,不如尝试使用或挂接
    LoadLibraryW
    。您可以用自定义逻辑完全替换该调用。你可以完成这个
    <?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>
    
    Configuration Properties -> Manifest Tool -> Input and Output -> Additional Manifest Files
    -->$(ProjectDir)\src\test.manifest