4

Win7/XP で WPF アプリケーションを実行しようとすると、非常に奇妙な問題に直面しています。WPF アプリケーションは .NET 4.0 を対象とし、Caliburn.Micro 1.5.2 および Autofac 3.1.0 を参照します

問題の概要から始めて、これまでに得たものの詳細を説明します。

概要

私の開発ワークステーションには、Windows 8 と Visual Studio 2012 があります。この投稿で説明されているように、Caliburn と Autofac を使用しています(基本的には、このの簡易版)。

開発マシンでアプリケーションをビルドして実行すると、すべてが期待どおりに進みます。ただし、バイナリを取得して Windows 7/XP マシンで実行すると、次のエラーが発生し、長いスタック トレースが表示されます。

System.InvalidOperationException: IoC is not initialized

環境間 (OS 以外) で確認できる唯一の違いは、私の開発ワークステーションには .NET 4.5 があり、Win7/XP のものには .NET 4.0 があることです。

詳細

簡単なアプリで問題を再現できました。ソリューション:

ここに画像の説明を入力

ShellViewModelはただの空Conductor<Screen>です。ShellViewがあるだけTextBlockです。

次のApp.xamlカリバーンの推奨事項:

<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のメソッドにいくつかのブレークポイントを設定しましたがGetInstance、それらは正しくヒットしています。

次に、バイナリ (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

このメッセージは、 Caliburn のソースIoCでわかるように、初期化前にクラスを呼び出したときに想定される動作であるようです。ただし、 はが を呼び出した直後に正しく初期化されます。ソースのメソッドを参照してください。IoCBootstrapperConfigureBootstrapperBase.StartRuntime

Bootstrapper からすべての依存性注入ロジックを削除すると、アプリは 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");
    }
}

考えているのですが、まだ意味がわかりません。

アップデート

サンプル アプリケーションへのリンク。

アップデート

観察された動作を分析した後、「IoC が初期化されていません」というエラーの原因は依存性注入ではなく、の起動前の呼び出しであるという結論に達しました (Sniffer が行ったように) 。MessageBox.ShowBootstrapper

MessageBox.Showエラーをファイルに書き込むように NLog のロギング ルーチンに変更したところ、実際の例外を突き止めることができました。本当の問題は、Autofac が PCL をターゲットにしており、.NET 4.0 Client Profile で問題なく実行できるようにするには、更新プログラム 4.0.3 をターゲット マシンにインストールする必要があることにあります。

しかし、元の問題はさておき、実はカリバーンには問題があります。Bootstrapper の初期化の前に 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()呼び出しており、この特定の順序で: Configure()then IoC.Get = GetInstanceand then IoC.GetAllInstances = GetAllInstances;and thenIoC.BuildUp = BuildUp;
  4. StartRuntime()呼び出し時Configure()にコードが実行されます。具体的には、すべてのメッセージ ボックスにオーナー ウィンドウがあり、デフォルトでオーナーが現在アクティブなアプリケーション ウィンドウであることを要求する(必須)MessageBox.Show()システム関数です。
  5. この段階で、システムコードの一部が実行されます。何が実行されているのかわかりませんが、システムによって実行されるコードの一部は、OnStartup()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);
});

また、Configure でエラーが発生した場合は、次の行を実行しないようにします。

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