可执行文件异常失败

本文关键字:失败 异常 可执行文件 | 更新日期: 2023-09-27 18:11:37

我使用的是ILMerge和Quartz。.NET在c# .NET 4.0 Windows服务应用程序中。该应用程序运行良好,不使用ILMerge,但现在我们即将发布,我想把所有的dll合并成一个单一的可执行文件。

问题是,ILMerge似乎工作得很好,但是当我运行合并后的可执行文件时,它抛出了这个异常:

未处理异常:石英。SchedulerException: ThreadPool类型为石英。无法实例化SimpleThreadPool。——>系统。InvalidCastException:无法强制转换类型为"quartz . simple"的对象。SimpleThreadPool'来输入'Quartz.Spi.IThreadPool'。
在Quartz.Util.ObjectUtils。InstantiateType[T](Type Type) in:line 0
在Quartz.Impl.StdSchedulerFactory.Instantiate()中:line 0
——内部异常堆栈跟踪结束——
在Quartz.Impl.StdSchedulerFactory.Instantiate()中:line 0
在Quartz.Impl.StdSchedulerFactory.GetScheduler()中:line 0

有人知道这是为什么吗?我已经浪费了4个多小时了,还是想不出来。如果我不与ILMerge合并,那么一切都运行良好(与石英.dll和Common.Logging.dll在同一目录中)。

我相信一定有人尝试过这样包装Quartz.net,有什么想法吗?

可执行文件异常失败

免责声明:我不了解Quartz。虽然我花了一些时间在ILMerge上。当我终于明白了它的局限性……我不再用它了。

ILMerge应用程序倾向于在包含"反射"一词的所有内容上出现问题。我可以猜测(我从未使用过Quartz.NET),一些类是使用反射解决的,并由配置文件驱动。

类不仅由其名称(带有名称空间)标识,而且由其来自的程序集标识(不幸的是,它不会在异常消息中显示)。因此,让我们假设(在ilmerge之前)您有两个程序集A(用于您的应用程序)和Q(用于Quartz.NET)。程序集"A"引用程序集"Q",并使用实现"Q:QIntf"的类"Q:QClass"。合并后,这些类变成了"A:QClass"answers"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'。

不理想但有效的解决方案是"嵌入"程序集而不是"合并"它们。我写了一个开源工具(嵌入而不是合并),但它与这个问题无关。所以,如果你决定嵌入,请直接问我。

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

您可以尝试创建自己的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。因为石英。Net在运行时动态加载一堆零碎的东西,沿着这条路走下去意味着你可能最终会重写相当多的东西。