在.NET中编译与版本无关的DLL
-
07-07-2019 - |
题
方案
我有两个围绕Microsoft Office的包装器,一个用于2003,一个用于2007.由于两个版本的Microsoft Office并排运行是“不正式可行”的。微软也没有推荐,我们有两个盒子,一个用Office 2003,另一个用Office 2007.我们分别编译包装器。这些DLL包含在我们的解决方案中,每个框都有相同的签出,但是Office 2003或2007“卸载”了。所以它不会尝试编译该特定的DLL。由于Office COM DLL不可用,如果不这样做将导致编译错误。
我们使用.NET 2.0和Visual Studio 2008。
事实
由于Microsoft在2007年神秘地更改了Office 2003 API,因此重命名和更改某些方法(叹息)从而使它们不向后兼容,我们需要两个包装器。 我们为每台构建机器提供了解决方案,并激活了一个Office DLL。例如:具有Office 2003的计算机具有“Office 2007”。 DLL已卸载,因此无法编译它。另一个盒子是相同的想法,但反过来。这一切都是因为我们不能在同一个盒子中有2个不同的Office用于编程。 (根据微软,你可以在技术上将两个Office放在一起),但不用于编程,并且没有一些问题。
问题
当我们更改应用程序版本(例如从1.5.0.1到1.5.0.2)时,我们需要重新编译DLL以匹配新版本的应用程序,这是自动完成的,因为Office包装器包含在解决方案中。由于包装器包含在解决方案中,因此它们继承了APP版本,但我们必须执行两次,然后“复制”。另一个DLL到创建安装程序的机器。 (痛苦......)
问题
是否可以编译一个可以与任何版本的应用程序一起使用的DLL,尽管它是“较旧的”?我已经阅读了一些关于清单的内容,但我从未与这些内容进行过互动。任何指针都将受到赞赏。
这个的秘密原因是我们没有在“年龄”中改变包装。微软也没有使用他们的古老API,但我们正在重新编译DLL以匹配我们制作的每个版本上的应用版本。我想自动完成这个过程,而不必依赖两台机器。
我无法从项目中删除DLL(它们都不是),因为存在依赖项。
我可以创建第三个“主包装器”但还没有考虑过。
有什么想法吗?有其他要求相同的人吗?
更新
澄清:
我有一个N项目的解决方案。
"申请表] + Office11Wrapper.dll + Office12Wrapper.dll。
两个“包装纸”使用应用程序的依赖关系+解决方案中的其他库(数据层,业务层,框架等)
每个包装器都有相应Office包(2003和2007)的引用。
如果我编译并且没有安装Office 12,则会从Office12Wrapper.dll中找不到Office 2007库时收到错误。 所以我拥有两台构建机器,一台使用Office 2003,一台使用Office 2007.在每台机器上进行完整的SVN更新+编译后,我们只需在“安装程序”中使用office12.dll。将包装器编译为“相同代码,相同版本”。
注意:Office 2007 Build Machine具有Office 2003的Wrapper“卸载”功能。反之亦然。
提前致谢。
解决方案
当.NET程序集解析程序无法在运行时找到引用的程序集时(在这种情况下,它找不到应用程序链接的特定包装DLL版本),其默认行为是失败并且实质上使应用程序崩溃。但是,可以通过挂钩AppDomain.AssemblyResolve事件来覆盖此行为。无论何时找不到引用的程序集,都会触发此事件,并且它使您有机会替换另一个程序集来代替缺少的程序集(前提是它们是兼容的)。因此,例如,您可以替换自己加载的旧版本的包装器DLL。
我发现这样做的最好方法是在挂钩事件的应用程序的主类上添加一个静态构造函数,例如:
using System.Reflection;
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
{
AssemblyName requestedName = new AssemblyName(e.Name);
if (requestedName.Name == "Office11Wrapper")
{
// Put code here to load whatever version of the assembly you actually have
return Assembly.LoadFile("Office11Wrapper.DLL");
}
else
{
return null;
}
}
}
通过将它放在主应用程序类的静态构造函数中,可以保证在任何代码尝试访问包装器DLL中的任何内容之前运行它,确保钩子提前到位。
您也可以使用策略文件进行版本重定向,但这往往更复杂。
其他提示
只是一个想法 - 您是否可以使用TlbExp创建两个互操作程序集(具有不同的名称和程序集),并使用接口/工厂通过您自己的界面对这两个进行编码?一旦你有了interop dll,你就不需要COM依赖了(当然除了测试等)。
TlbImp对该版本有/ asmversion,因此它可以作为构建脚本的一部分完成;但我相信你甚至需要这个:只要确保“特定版本”即可。在引用中是否为false(解决方案资源管理器)?
另外 - 我知道它没有帮助,但C#4.0带有 dynamic
和/或“No PIA”可能会有所帮助(未来;也许)。
我不确定我是否完全遵循你所说的一切,但让我试试:
听起来你有两个(?)项目的解决方案。一个是实际的应用程序,另一个是Office API的包装器。然后,您的应用程序具有对Office API包装器的项目引用。我以前从来没有为办公室编程,但听起来编程API是一个常见的组件,你只能在一台机器上有一个版本(即2003或2007,而不是两者)。也许这就是问题所在,但是因为你有一个项目引用,所以首先编译包装器,复制到你的应用程序的bin目录,你的应用程序将链接到包装器的构建。这将导致应用程序的清单在运行时专门请求该包装器的版本。
如果您在单独的解决方案中使用了包装器,并添加了对已编译库而不是项目的引用,那么您始终会将应用程序链接到该包装器的版本,并且可以避免此问题。
另一种可能的选择是装配绑定重定向。这是更先进的,并附带它自己的一组问题,但你可以阅读它此处。
或者类似于Marc的想法,您可以提取一个接口并将一些常见对象拉入Framework库,并针对接口和公共对象对您的应用程序进行编码。然后在运行时使用反射加载程序集并实例化所需的包装器。
我认为关键是如果可以的话删除项目依赖项。这听起来像包装器非常稳定并且不会改变,否则你不会要求链接到它的先前版本。
在同一台计算机上并排安装Office 2003和2007 绝对可以 - 我们甚至在最终用户生产工作站上也在我们的组织中进行。
在该链接文章中,Microsoft建议您不要将其用于实际用途。但在你的情况下,它似乎只适用于单个构建机器,即你不会在该机器上实际使用任何一个版本的Office。在这种情况下,我会试着看看你是否可以进行并排安装。
我的假设可能是错的,你试图为每个开发人员的机器执行此操作。在这种情况下,你应该忽略这个答案: - )
漂亮的模具!我只是将基于上述概念的实现整合在一起,它的工作非常好:
static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string partialName = args.Name.Substring(0, args.Name.IndexOf(','));
return Assembly.Load(new AssemblyName(partialName));
}
当然还有增强空间,但这对我来说很有用!