4

我在尝试在 Win7/XP 上运行 WPF 应用程序时遇到了一个非常奇怪的问题。WPF 应用程序以 .NET 4.0 为目标,它引用了 Caliburn.Micro 1.5.2 和 Autofac 3.1.0

我将从对问题的总结开始,然后我将详细介绍我到目前为止所获得的内容。

概述

在我的开发工作站中,我有 Windows 8 和 Visual Studio 2012。我正在使用 Caliburn 和 Autofac,如本文所述(基本上是 this简化版本)。

当我在我的开发机器中构建和运行应用程序时,一切都按预期进行。但是,当我获取二进制文件并在 Windows 7/XP 机器上执行它们时,我收到以下错误,并带有很长的堆栈跟踪:

System.InvalidOperationException: IoC is not initialized

我可以看到的环境之间(除了操作系统)的唯一区别是我的开发工作站具有 .NET 4.5,而 Win7/XP 具有 .NET 4.0。

细节

我能够通过一个简单的应用程序重现该问题。解决方案:

在此处输入图像描述

ShellViewModel只是一个空的Conductor<Screen>ShellView只有一个TextBlock

以下App.xamlCaliburn 建议:

<Application x:Class="WpfApplication2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication2">
    <Application.Resources>
        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:Bootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

修改了App.xaml.cs代码以捕获并显示异常:

public partial class App : Application
{

    public App ()
    {
        // hook on error before app really starts
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        try
        {
            this.InitializeComponent();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
            throw;
        }
    }

    public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        MessageBox.Show(((Exception)e.ExceptionObject).ToString());
    }
}

有趣的部分是Bootstrapper. 如前所述,我有一个应用程序启动并运行,与 Caliburn 和 Autofac 一起工作,与Bootstrapper描述的类似here。例如,我创建了一个简化版本:

public class Bootstrapper : Bootstrapper<ShellViewModel>
{
    private IContainer container;
    protected IContainer Container
    {
        get { return this.container; }
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            if (container.IsRegistered(serviceType))
                return container.Resolve(serviceType);
        }
        else
        {
            if (container.IsRegisteredWithName(key, serviceType))
                container.ResolveNamed(key, serviceType);
        }
        throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? serviceType.Name));
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return this.Container.Resolve(typeof(IEnumerable<>).MakeGenericType(serviceType)) as IEnumerable<object>;
    }

    protected override void BuildUp(object instance)
    {
        this.Container.InjectProperties(instance);
    }

    protected override void Configure()
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<ShellViewModel>();

        builder.RegisterAssemblyTypes(this.GetType().Assembly);

        builder.RegisterType<WindowManager>()
            .As<IWindowManager>()
            .SingleInstance();

        builder.RegisterType<EventAggregator>()
            .As<IEventAggregator>()
            .SingleInstance();

        this.container = builder.Build();
    }
}

在我的工作站中,它运行得很好:

在此处输入图像描述

我在Bootstrapper'sGetInstance方法中放置了一些断点,它们被正确命中。

然后,我获取了二进制文件(bin/Debug)并尝试在 Windows XP/7 虚拟机中运行它们。该应用程序无法启动,并且出现以下异常:

System.InvalidOperationException: IoC is not initialized.

   at Caliburn.Micro.IoC.<.cctor>b__0(Type service, String key) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\IoC.cs:line 13

   at Caliburn.Micro.IoC.Get[T](String key) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\IoC.cs:line 32

   at Caliburn.Micro.BootstrapperBase.DisplayRootViewFor(Type viewModelType, IDictionary`2 settings) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\Bootstrapper.cs:line 254

   at Caliburn.Micro.BootstrapperBase.DisplayRootViewFor[TViewModel](IDictionary`2 settings) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\Bootstrapper.cs:line 264

   at Caliburn.Micro.Bootstrapper`1.OnStartup(Object sender, StartupEventArgs e) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\Bootstrapper.cs:line 288

   at System.Windows.Application.OnStartup(StartupEventArgs e)

   at System.Windows.Application.<.ctor>b__1(Object unused)

   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)

   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

   at System.Windows.Threading.DispatcherOperation.InvokeImpl()

   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)

   at System.Threading.ExecutionContext.runTryCode(Object userData)

   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)

   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)

   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

   at System.Windows.Threading.DispatcherOperation.Invoke()

   at System.Windows.Threading.Dispatcher.ProcessQueue()

   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)

   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)

   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)

   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

   at MS.Win32.UnsafeNativeMethods.MessageBox(HandleRef hWnd, String text, String caption, Int32 type)

   at System.Windows.MessageBox.ShowCore(IntPtr owner, String messageBoxText, String caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options)

   at System.Windows.MessageBox.Show(String messageBoxText)

   at WpfApplication2.App..ctor() in c:\Users\Public\Projetos\Outros\WpfApplication3\WpfApplication2\App.xaml.cs:line 27

   at WpfApplication2.App.Main() in c:\Users\Public\Projetos\Outros\WpfApplication3\WpfApplication2\obj\Debug\App.g.cs:line 0

该消息显然是在IoC初始化之前调用该类时的预期行为,正如我们在Caliburn 的源代码中看到的那样。但是,在调用.IoC之后立即正确初始化. 请参阅源代码中的方法。BootstrapperConfigureBootstrapperBase.StartRuntime

如果我从引导程序中删除所有依赖注入逻辑,则该应用程序在 Win XP/7 上运行良好。

我花了一些时间试图找出究竟是什么触发了这种行为。我从 中删除了所有内容Bootstrapper,经过一些尝试,以下是触发问题所需的全部内容:

public class Bootstrapper : Bootstrapper<ShellViewModel>
{
    protected override void Configure()
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<ShellViewModel>();
    }
}

如果我评论该行builder.RegisterType<ShellViewModel>();,则该应用程序有效。

结论:在 Autofac ContainerBuilder 中注册任何内容的简单行为都会触发这些行为。我什至不需要使用它。我对此完全感到困惑。

我在这个问题上花了几个小时。我真的很想使用 Caliburn 和 Autofac,因为我都喜欢它们。如果有人能对此有所了解,我将不胜感激。

更新

我注意到,如果我在 Bootstrapper.Configure 方法中调用 MessageBox.Show,即使在我的 Win8 中使用 VS2012 进行调试,观察到的行为(“IoC 未初始化”)也会发生:

public class Bootstrapper : Bootstrapper<ShellViewModel>
{
    protected override void Configure()
    {
        MessageBox.Show("Configure");
    }
}

我正在考虑它,但我还不知道它是什么意思。

更新

示例应用程序的链接。

更新

在分析观察到的行为后,我得出结论(就像 Sniffer 所做的那样)“IoC 未初始化”错误的原因不是依赖注入,而是在启动MessageBox.Show之前调用Bootstrapper

我更改了MessageBox.ShowNLog 的日志记录例程以将错误写入文件,这样我就能够追踪到真正的异常。真正的问题来自 Autofac 以 PCL 为目标,并且要使其与 .NET 4.0 Client Profile 一起正常运行,我需要在目标机器中安装更新 4.0.3

然而,抛开最初的问题,Caliburn 确实存在问题。在引导程序初始化之前调用 MessageBox.Show 似乎会触发一个全新的应用程序启动过程,该过程发生在 IoC 配置之间,产生观察到的异常。

我认为当前的问题已经偏离了最初的目的,我认为应该关闭它。我将在不受我的特定应用程序问题影响的环境中创建一个针对 Caliburn.Micro 问题的新问题。

4

2 回答 2

1

好吧,我在我的 XP 机器上运行了您的代码,并且您上次更新时遇到了问题,您使用MessageBox该类并导致您在 Windows 8 机器上出现问题,但是之前的代码是您创建一个新容器并注册ShellViewModel哪个你说在你的 win 7/xp 机器上造成问题并没有给我造成任何问题(它编译并运行正常)

现在我想知道为什么在MessageBox.Show()方法中使用该Configure方法会导致该异常,我想出了原因,归结为:

  1. Caliburn.Micro 的(之后的 CM) Bootstrapper<TRootModel>构造函数正在调用Start()
  2. Start()正在打电话StartRuntime()
  3. StartRuntime()正在调用 and 以这个特定的顺序:Configure()然后IoC.Get = GetInstance,然后IoC.GetAllInstances = GetAllInstances;,然后IoC.BuildUp = BuildUp;
  4. StartRuntime()调用Configure()你的代码被执行时,特别MessageBox.Show()是它是一个系统函数,它要求 (必须)每个消息框都有一个所有者窗口,并且默认情况下所有者是你当前活动的应用程序窗口。
  5. 现在在这个阶段运行一段系统代码,我不知道正在运行什么,但是系统执行的这段代码调用了OnStartup()方法 override,CM 在引导程序中覆盖了该方法并使用它来显示视图供TRootModel您选择。
  6. 为了让 Caliburn 显示TRootModel它需要一个实例的视图IWindowManager并获得它使用(你想通了)我们心爱的东西IoC,正如你从第 3 步中看到的那样,它还没有初始化,它仍然停留在那个Configure()方法上并没有继续前进。

总结:容器配置的代码在我的Win XP机器上运行没有问题,但是覆盖MessageBox.Show()方法的代码没有,我给你详细解释了原因。Configure()

于 2013-07-13T00:23:37.323 回答
0

使用这样的代码来显示来自引导程序的消息:

Execute.OnUIThread(() =>
{
    MessageBox.Show(m);
});

如果在配置中发生错误,也请不要执行此行:

DisplayRootViewFor<MainViewModel>();
于 2017-02-20T20:48:56.123 回答