I'm facing a quite strange problem trying to run a WPF application on Win7/XP. The WPF application targets .NET 4.0 and it references Caliburn.Micro 1.5.2 and Autofac 3.1.0.
I'm going to start with a summary of the problem and then I'm going to give details about what I've got so far.
OVERVIEW
In my dev workstation I have Windows 8 and Visual Studio 2012. I'm using Caliburn and Autofac as described in this post (basically a simplified version of this).
When I build and run the application in my dev machine, everything goes as expected. However, when I take the binaries and execute them in a Windows 7/XP machine, I get the following error, with a long stacktrace:
System.InvalidOperationException: IoC is not initialized
The only difference I can see between the environments (besides the OS) is that my dev workstation has .NET 4.5, while the Win7/XP ones have .NET 4.0.
DETAILS
I was able to reproduce the issue with a simple application. The solution:
ShellViewModel
is just a empty Conductor<Screen>
. ShellView
has just a TextBlock
.
The App.xaml
follows Caliburn recomendations:
<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>
The App.xaml.cs
code was modified to catch and show exceptions:
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());
}
}
The fun part is the Bootstrapper
. As a said, I have an app up and running with Caliburn and Autofac working together with a Bootstrapper
similar to the one described here
. For the example, I created a simplified version:
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();
}
}
In my workstation, it runs just fine:
I've put some breakpoints in the Bootstrapper
's GetInstance
methods and they are being correctly hit.
Then, I took the binaries (bin/Debug) and tried to run them in a Windows XP/7 virtual machine. The app does not start and I get the following exception:
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
That message apparentely is the expected behavior when calling the IoC
class before its initialization, as we can see in Caliburn's source. However, the IoC
is correctly initialized right after the Bootstrapper
's call to Configure
. See BootstrapperBase.StartRuntime
method in the source.
If I remove all the dependency injection logic from the Bootstrapper, the app runs fine on Win XP/7.
I spent sometime trying to find what exactly was triggering this behavior. I removed everything from the Bootstrapper
and after some attempts, the following is all it takes to trigger the problem:
public class Bootstrapper : Bootstrapper<ShellViewModel>
{
protected override void Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<ShellViewModel>();
}
}
If I comment the line builder.RegisterType<ShellViewModel>();
, the app works.
Conclusion: the simple act of registering anything in an Autofac ContainerBuilder triggers the behaviors. I don't even need to use it. I'm totally puzzled by that.
I've spent hours on this problem. I really want to use Caliburn and Autofac, because I like them both. If anyone could shed some light on it, I'd appreciate.
UPDATE
I've noticed that if I make a call to MessageBox.Show in the Bootstrapper.Configure method the observed behavior ("IoC is not initialized") will happen even debugging with VS2012 in my Win8:
public class Bootstrapper : Bootstrapper<ShellViewModel>
{
protected override void Configure()
{
MessageBox.Show("Configure");
}
}
I'm thinking about it, but I don't know yet what it means.
UPDATE
Link for the sample application.
UPDATE
After analysing the observed behaviors, I came to the conclusion (as Sniffer did) that the cause of the "IoC is not initialized" error is not the dependency injection, but calling MessageBox.Show
before the Bootstrapper
's startup.
I changed the MessageBox.Show
to NLog's logging routines to write errors to a file, and with that I was able track down the real exception. The real problem comes from the fact that Autofac targets PCL and for it to run allright with .NET 4.0 Client Profile, I need to install the update 4.0.3 in target machine.
However, putting the original problem aside, there is in fact a problem with Caliburn. Calling MessageBox.Show before the Bootstrapper's initialization seems to trigger a whole new Application start process that happens between the IoC configuration, generating the observed exception.
I believe the current question has deviated from its original purpose and I think it should be closed. I will create a new question that targets the Caliburn.Micro problem in a environment not affected by my specific application's problems.