Windows 防止MFC ActiveX控件使用已加载到进程中的DLL
简短版本:通过Internet Explorer加载到网页中的MFC ActiveX控件如何保证从其自己的目录加载与其关联的DLL,而不是拾取可能已加载到进程中的同名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
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;