可执行文件因奇怪的异常而失败

时间:2013-05-01 18:12:03

标签: c# .net quartz.net ilmerge

我在C#.NET 4.0 Windows服务应用程序中使用ILMerge和Quartz.NET。该应用程序在没有使用ILMerge的情况下运行良好,但现在我们即将发布,我想将所有DLL组合成一个可执行文件。

问题是,ILMerge似乎运行正常,但是当我运行组合的可执行文件时,它会抛出此异常:

  

未处理的异常:Quartz.SchedulerException:无法实例化ThreadPool类型“Quartz.Simpl.SimpleThreadPool”。 ---> System.InvalidCastException:无法将类型为“Quartz.Simpl.SimpleThreadPool”的对象强制转换为“Quartz.Spi.IThreadPool”。
  at Quartz.Util.ObjectUtils.InstantiateType [T](Type type)in:line 0
  在Quartz.Impl.StdSchedulerFactory.Instantiate()中:第0行   ---内部异常堆栈跟踪结束---
  在Quartz.Impl.StdSchedulerFactory.Instantiate()中:第0行   在Quartz.Impl.StdSchedulerFactory.GetScheduler()中:第0行

有谁知道为什么会这样?我已经浪费了4个多小时,我无法弄明白。如果我不与ILMerge结合,那么一切运行正常(Quartz.dll和Common.Logging.dll位于同一目录中)。

我确定有人必须尝试过像这样包装Quartz.net,任何想法吗?

2 个答案:

答案 0 :(得分:1)

您可以尝试创建自己的ISchedulerFactory,并避免使用反射来加载所有类型。 StdSchedulerFactory使用此代码创建线程池。这就是你的错误发生的地方,并且是开始寻求改变的地方:

        Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool);

        try
        {
            tp = ObjectUtils.InstantiateType<IThreadPool>(tpType);
        }
        catch (Exception e)
        {
            initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e);
            throw initException;
        }

调用的ObjectUtils.InstantiateType方法是这一个,最后一行是抛出异常的那个:

    public static T InstantiateType<T>(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type", "Cannot instantiate null");
        }
        ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
        if (ci == null)
        {
            throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
        }
        return (T) ci.Invoke(new object[0]);
    }

在工厂的这一部分之后,数据源使用相同的模式加载,然后作业本身也会动态加载,这意味着您还必须编写自己的JobFactory。由于Quartz.Net在运行时动态地加载了一堆零碎的东西,所以这条道路意味着你最终可能会重写相当多的东西。

答案 1 :(得分:1)

免责声明:我根本不了解Quartz.NET,尽管我花了一些时间与ILMerge斗争。当我终于明白它的局限性时...我就停止使用了它。

ILMerge的应用程序往往会出现包含“反射”一词的所有内容的问题。 我猜(我从未使用过Quartz.NET)有些类是使用反射解析的,并且是由配置文件驱动的。

类不仅通过其名称(使用命名空间)来标识,而且还通过它来自的汇编来标识(不幸的是它不会在异常消息中显示)。 所以,让我们假设您(在ILMerging之前)有两个程序集A(对于您的应用程序)和Q(对于Quartz.NET)。 程序集“A”引用了程序集“Q”,并使用了一个实现“Q:QIntf”的类“Q:QClass”。 合并后,这些类变为'A:QClass'和'A:QIntf'(它们从汇编Q移到A)并且代码中的所有引用都被替换为使用那些(完全)新的类/接口,所以“A” :QClass“正在实施”A:QIntf“。 但是,它没有改变任何仍然可以引用“Q:QClass”的配置文件/嵌入字符串。

因此,当应用程序正在读取那些未更新的配置文件时,它仍会加载“Q:QClass”(为什么它会发现它是一个不同的问题,也许你在当前文件夹中留下了程序集'Q',或者它可能在GAC中 - 见1)。 无论如何,“Q:QClass”没有实现“A:QIntf”,它仍然实现“Q:QIntf”,即使它们是二进制相同的 - 所以你不能将'Q:QClass'转换为'A:QIntf'。< / p>

不理想但工作的解决方案是“嵌入”组件而不是“合并”它们。我写了一个开源工具(嵌入而不是合并),但它与这个问题无关。所以如果你决定嵌入就问我。

  1. 您可以通过删除(隐藏,适用于您的任何内容)PC上的每个Q.dll实例来测试它。如果我是对的,那么例外现在应该说'FileNotFound'。