我在尝试在 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.xaml
Caliburn 建议:
<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
之后立即正确初始化. 请参阅源代码中的方法。Bootstrapper
Configure
BootstrapperBase.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.Show
NLog 的日志记录例程以将错误写入文件,这样我就能够追踪到真正的异常。真正的问题来自 Autofac 以 PCL 为目标,并且要使其与 .NET 4.0 Client Profile 一起正常运行,我需要在目标机器中安装更新 4.0.3。
然而,抛开最初的问题,Caliburn 确实存在问题。在引导程序初始化之前调用 MessageBox.Show 似乎会触发一个全新的应用程序启动过程,该过程发生在 IoC 配置之间,产生观察到的异常。
我认为当前的问题已经偏离了最初的目的,我认为应该关闭它。我将在不受我的特定应用程序问题影响的环境中创建一个针对 Caliburn.Micro 问题的新问题。