Windows 防止MFC ActiveX控件使用已加载到进程中的DLL

Windows 防止MFC ActiveX控件使用已加载到进程中的DLL,windows,dll,mfc,activex,manifest,Windows,Dll,Mfc,Activex,Manifest,简短版本:通过Internet Explorer加载到网页中的MFC ActiveX控件如何保证从其自己的目录加载与其关联的DLL,而不是拾取可能已加载到进程中的同名DLL 长版本,细节血淋淋的:我有一个应用程序myapp.exe,它使用一组dll:one.dll,two.dll和thire.dll MFC ActiveX控件也使用相同的DLL,该控件公开了一些与myapp.exe相同的功能,因此还有一个mycontrol.ocx也与这些DLL链接。ActiveX控件与应用程序/myappMIM

简短版本:通过Internet Explorer加载到网页中的MFC ActiveX控件如何保证从其自己的目录加载与其关联的DLL,而不是拾取可能已加载到进程中的同名DLL

长版本,细节血淋淋的:我有一个应用程序
myapp.exe
,它使用一组dll:
one.dll
two.dll
thire.dll

MFC ActiveX控件也使用相同的DLL,该控件公开了一些与
myapp.exe
相同的功能,因此还有一个
mycontrol.ocx
也与这些DLL链接。ActiveX控件与
应用程序/myapp
MIME类型相关联,因此IE将使用它来显示使用
myapp.exe
创建的文档

可以同时安装
myapp.exe
的版本1和版本2,但只有最新版本的
mycontrol.ocx
(版本2)与
应用程序/myapp
MIME类型相关联:

c:\Program Files\MyApp\Version 1\
                                 myapp.exe
                                 mycontrol.ocx
                                 one.dll
                                 two.dll
                                 three.dll
c:\Program Files\MyApp\Version 2\
                                 myapp.exe
                                 mycontrol.ocx  <-- registered with the MIME type
                                 one.dll
                                 two.dll
                                 three.dll
c:\Program Files\MyApp\Version 1\
myapp.exe
mycontrol.ocx
1.dll
2.dll
3.dll
c:\Program Files\MyApp\Version 2\
myapp.exe

mycontrol.ocx尝试的一个选项可能是将ocx的DLL依赖项指定为延迟加载,然后编写自己的延迟加载辅助函数,该函数使用LoadLibraryEx()确保从要使用的路径加载DLL。我在过去曾对此进行过实验,并将其用于可执行文件,但我不明白为什么它不能用于OCX

MSDN在这方面有很好的文档。在delay load helper函数中,您希望将注意力集中在“dliNotePreLoadLibrary”的情况,并返回使用LoadLibraryEx()获得的正确DLL的HMODULE


顺便说一下,最后我没有使用这种方法:我只是确保所有DLL中都有版本号。最后,这是最简单的方法…

了解一点DLL地狱,我对“MFC激活上下文”一无所知。这就是为什么你上面的#2想法看起来很可靠

但如果这对你不起作用,我会尝试研究三种可能的解决方案:

  • 疯狂的想法。当您在注册表中注册“mycontrol.ocx”与application/myapp关联时,您当前已通过其完整路径(c:\program files\app\version2\mycontrol.ocx)注册它。只需将其注册为“mycontrol.ocx”,而不指定目录。如果您幸运,IE control将“LoadLibrary”(“mycontrol.ocx”)并从与EXE相同的目录中找到它。如果您确实需要在IE的独立实例中加载mycontrol.ocx,则这当然会中断。但是,您可能可以为外部页面使用备用MIME类型(或com guid)来直接加载控件

  • 在应用程序的安装中,将EXE放在与DLL不同的目录中。然后使用虚拟地将特定DLL目录添加到应用程序的路径中。唯一的问题是,我认为您不能使用完全限定的路径设置AppPath密钥名。但是我知道你可以有一个EXE名称,它有一个指向不同名称EXE的完全限定路径。因此,我们可以将“myapp.exe”的第二个实例“虚拟重命名”为“myapp2.exe”。在桌面快捷方式和“开始”菜单入口点中,它们都会启动“myapp2.exe”,但会被重定向到“version2\app\myapp.exe”

  • 在您的EXE和DLL中使用和适当的清单


  • 下面是我最后如何解决这个问题的:

    我将
    mycontrol.ocx
    分为两部分,一部分是实现该功能的
    mycontrol.dll
    ,另一部分是实现注册逻辑并充当实际模块垫片的最小
    mycontrol.ocx
    。垫片
    mycontrol.ocx
    没有DLL依赖项。它的作用如下:

    • 如果在启动进程的可执行文件旁边有一个
      mycontrol.dll
      ,则这必须是一个新版本
      myapp.exe
      ,垫片加载
      mycontrol.dll
      ,并反映
      DllGetClassObject
      DllCanUnloadNow
      对它的调用

    • 如果在可执行文件旁边有一个
      mycontrol.ocx
      但没有
      mycontrol.dll
      ,则这必须是一个旧版本的
      myapp.exe
      ,因此垫片加载该
      mycontrol.ocx
      ,并重定向到该版本

    • 如果两个模块都不存在,则这必须是web浏览器或其他ActiveX主机,因此垫片将加载与其自身位于同一目录中的
      mycontrol.dll
      ,并重定向到该目录


    这比现实生活中的情况要复杂一些,但结果是每个人都加载了他们希望加载的代码,并且一切正常。

    #2上面的内容最有意义,但我不知道你所说的“MFC现在将每个DLL视为自己的激活上下文,这是不对的”。什么是激活上下文?为什么这很重要?+1是一个很好的主意,但遗憾的是我不能使用它。我的代码广泛使用了导出C++类,其中<代码>一.dll <代码>使用<代码> TwoClass <代码>,导出代码从< > dll >代码>。这使得
    one.dll
    two.dll
    导入数据符号
    TwoClass::vftable
    ,因此在指定
    /delayload:two.dll
    时链接失败,因为导入数据符号会阻止延迟加载。(有趣的是,它在调试版本中工作——vtables在那里一定不同。如果我能找到一种方法,让发布版本将vtables与调试版本一样对待,我可能没问题,但我不能。)哎哟,真讨厌。可惜微软没有想出一个合理的方法来延迟加载
    c:\Program Files\MyApp\Version 1\app\
                                          myapp.exe
    c:\program files\MyApp\Version 1\dll\
                                          mycontrol.ocx
                                          one.dll
                                          two.dll
                                          three.dll
    
    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\myapp.exe
          (default)=c:\program files\MyApp\Version1\app\myapp.exe (type == REG_EXPAND_SZ)
          Path=c:\program files\MyApp\Version1\dll;c:\program files\MyApp\Version1\app;
    
    
    
    c:\program files\MyApp\Version 2\app\
                                         myapp.exe
    c:\program files\MyApp\Version 2\dll\
                                          mycontrol.ocx
                                          one.dll
                                          two.dll
                                          three.dll
    
    
    // NOTICE THE VIRTUAL RENAME TO MYAPP2 in the line below
    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\myapp2.exe 
          (default)=c:\program files\MyApp\Version 2\app\myapp.exe (type == REG_EXPAND_SZ)
          Path=c:\program files\MyApp\Version 2\dll;c:\program files\MyApp\Version 2\app;