10

设想

我有两个围绕 Microsoft Office 的包装器,一个用于 2003,一个用于 2007。由于同时运行两个版本的 Microsoft Office 是“不可能的”,也不是 Microsoft 推荐的,所以我们有两个盒子,一个带有 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 个项目的 1 个解决方案。

“应用程序”+ 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,反之亦然。

提前致谢。

4

5 回答 5

19

当 .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 中的任何内容之前运行,从而确保挂钩提前到位。

您还可以使用策略文件进行版本重定向,但这往往更复杂。

于 2008-11-12T16:19:00.650 回答
2

只是一个想法-您可以使用 TlbExp 创建两个互操作程序集(具有不同的名称和程序集),并使用接口/工厂通过您自己的接口对两者进行编码吗?一旦你有了互操作 dll,你就不需要 COM 依赖(当然除了测试等)。

TlbImp 有一个版本的 /asmversion,所以它可以作为构建脚本的一部分来完成;但我敢肯定你甚至需要这个:只需确保参考(解决方案资源管理器)中的“特定版本”是错误的?

另外 - 我知道它没有帮助,但是带有dynamic和/或“无 PIA”的 C# 4.0 可能会有所帮助(将来;也许)。

于 2008-11-10T13:19:15.477 回答
2

我不确定我是否完全遵循你所说的一切,但让我试试:

听起来您有一个包含 2 个(?)项目的解决方案。一个是实际应用程序,另一个是 Office API 的包装器。然后,您的应用程序具有对您的 Office API 包装器的项目引用。我以前从未为 Office 编程过,但听起来编程 API 是一个通用组件,您只能在机器上拥有一个版本(即 2003 或 2007,而不是两者)。也许这就是问题所在,但是因为您有一个项目引用,所以包装器将首先被编译,复制到您的应用程序的 bin 目录,您的应用程序将链接到该包装器的构建。这将导致应用程序的清单在运行时专门请求该版本的包装器。

如果您将包装器放在单独的解决方案中,并添加了对已编译库而不是项目的引用,您将始终将您的应用程序链接到该包装器版本,您可以避免该问题。

另一种可能的选择是程序集绑定重定向。这是更高级的,并带有它自己的一组问题,但你可以在这里阅读。

或者类似于 Marc 的想法,您可以提取一个接口并将一些通用对象拉入一个框架库,然后针对接口和通用对象编写您的应用程序。然后在运行时使用反射来加载程序集并实例化您想要的包装器。

我认为关键是如果可以的话,删除项目依赖项。听起来包装器非常稳定并且没有改变,否则您不会要求链接到它的先前版本。

于 2008-11-10T13:50:58.750 回答
1

在同一台机器上并排安装 Office 2003 和 2007绝对是可能的- 我们在我们的组织中甚至在最终用户的生产工作站上也这样做。

在链接的文章中,Microsoft 建议您不要在实际使用中执行此操作。但在您的情况下,它似乎只适用于单个构建机器,即您不会在该机器上实际使用任一版本的 Office。在这种情况下,我会尝试看看您是否可以进行并排安装。

我的假设可能是错误的,您正在尝试为每个开发人员的机器执行此操作。在这种情况下,你应该忽略这个答案:-)

于 2008-11-10T14:16:14.643 回答
0

不错的侦探工作!我只是根据上面介绍的概念拼凑了一个实现,它工作得非常好:

static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
     string partialName = args.Name.Substring(0, args.Name.IndexOf(','));
     return Assembly.Load(new AssemblyName(partialName));
}

当然还有改进的空间,但这对我有用!

于 2009-06-16T17:12:51.617 回答