简体   繁体   中英

IoC is not initialized on Win7/XP

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.

Well i ran your code on my XP machine and had a problem with your last update, the one where you used the MessageBox class and it caused you issues on your windows 8 machine, but the previous code where you create a new container and register the ShellViewModel which you said caused problems on your win 7/xp machine did not cause me any problems (it compiled and ran OK) .

Now i was wondering why using the MessageBox.Show() method inside the Configure method is causing that exception and i kind of figured out the reason for that and it boils down to this:

  1. Caliburn.Micro's (CM from here after) Bootstrapper<TRootModel> constructor is calling Start()
  2. Start() is calling StartRuntime()
  3. StartRuntime() is calling and in this particular order: Configure() then IoC.Get = GetInstance and then IoC.GetAllInstances = GetAllInstances; and then IoC.BuildUp = BuildUp;
  4. When StartRuntime() calls Configure() your code is executed, specifically MessageBox.Show() which is a system function that requires ( a must ) that every message box have an owner window and the owner by default is your current active application window.
  5. Now at this stage a piece of system code is run, i don't know what is being run but the piece of code that is executed by the system calls the OnStartup() method override which CM does override in the bootstrapper and uses it to show the view for the TRootModel of your choosing.
  6. In order for Caliburn to show the view for that TRootModel it needs an instance of IWindowManager and to get that it uses ( you figured it out ) our beloved IoC which as you see from step number 3, it has not initialized yet, it still stuck on that Configure() method and hasn't moved on.

Summary: The code with the container configuration works with no problems on my Win XP machine but the code with the MessageBox.Show() method in the Configure() override does not and i gave you a detailed explanation of the reason.

use code like this to show a message from the bootstrapper:

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

Also keep this line from executing if the error happens in Configure:

DisplayRootViewFor<MainViewModel>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM