首页 文章

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

提问于
浏览
0

Short version :如何通过Internet Explorer将MFC ActiveX控件加载到网页中,保证其关联的DLL是从其自己的目录加载的,而不是选择可能已经加载到进程中的同名DLL?

Long version, with gory detail :我有一个使用一组DLL的应用程序 myapp.exeone.dlltwo.dllthree.dll .

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

可以安装 myapp.exe 的版本1和版本2,但只有最新版本的 mycontrol.ocx (版本2)与 application/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

这就是它变得困难的地方: myapp.exe 有一个用于显示Web内容的嵌入式Internet Explorer控件 . 您可以运行 myapp.exe 的版本1,将嵌入式Internet Explorer指向 application/myapp 文档,IE将加载 mycontrol.ocx 的版本2以查看它 . 这应该没问题,但事实并非如此:

会发生什么情况是Windows加载 mycontrol.ocx ,发现它依赖于 one.dll 并且该过程中已经存在 one.dll ,并将 mycontrol.ocx 的导入表指向已经加载的(版本1) one.dll ,而不是加载版本2 one.dll . 这是失败的,因为 mycontrol.ocx 的版本2使用版本1中没有的 one.dll 版本2中的新API .

How do I stop it doing that? 如果 one.dll 尚未加载,Windows会查找 c:\Program Files\MyApp\Version 2 ,一切都会好的 . (而且我无法为每个版本的软件重命名我的所有模块!)

Failed solution #1 :我给了 mycontrol.ocx 一个清单,为所有DLL指定 <file ...> 元素,但这不起作用 . one.dllVersion 2 目录加载,但 two.dll 没有 . 我假设这是因为 two.dll 正在加载 one.dll 引用它,而 one.dll 没有这样的清单 .

Failed solution #2 :所以我在 all 中添加了一个清单,每个清单都列出了 <file ...> 元素中的依赖关系 . 但这完全破坏了 myapp.exe ,因为MFC现在将每个DLL视为拥有自己的激活上下文并且这是不正确的 - 整个过程(在 myapp.exe 的情况下)应该有一个MFC上下文或控件的整个实例(在案件 mycontrol.exe ) . 我不能为每个模块提供新的激活上下文;我需要一个整个 mycontrol.ocx 及其附带的DLL .

我更改了所有清单以使用相同的 name= 属性,希望这会将它们全部放在同一个上下文中,但这没有任何效果 .

Failed solution #3 :我给 myapp.exe 一个表示 <file name="mycontrol.ocx"/> 的清单,希望强制该应用程序的版本1使用控件的版本1,这没关系 . 但这失败了因为当Internet Explorer加载 mycontrol.ocx 时,它使用 mycontrol.ocx 版本2的完整路径调用 LoadLibrary ,并且当使用完整路径加载模块时,清单重定向不起作用 .

Something I can't do :我可以't change the code that loads the control, because it'的Internet Explorer这样做(通过MIME类型) .

我们将非常感激地提出任何解决方案,建议或简单的同情信息 .

3 回答

  • 0

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

    MSDN在using the delay loading helper function上有很好的文档 . 在延迟加载辅助函数中,您希望专注于"dliNotePreLoadLibrary"情况,并返回使用LoadLibraryEx()获得的正确DLL的HMODULE .

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

  • 0

    知道一下DLL地狱,我对“MFC激活上下文”一无所知 . 这就是为什么你上面的#2想法看起来很稳固的原因 .

    但是,如果这不起作用,我会尝试和调查三种可能的解决方案:

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

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

    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;
    
  • 1

    Here's how I solved this in the end:

    我将 mycontrol.ocx 分成两部分, mycontrol.dll 实现了功能,最小的 mycontrol.ocx 实现了注册逻辑,并充当真实模块的垫片 . 垫片 mycontrol.ocx 没有DLL依赖项 . 这是它的作用:

    • 如果启动进程的可执行文件旁边有 mycontrol.dll ,则必须是新版本 myapp.exe ,并且shim加载 mycontrol.dll 并反映 DllGetClassObjectDllCanUnloadNow 调用 .

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

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

    它比现实生活中的复杂一点,但结果是每个人都加载了他们期望加载的代码,而且一切正常 .

相关问题