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
でわかるように、初期化前にクラスを呼び出したときに想定される動作であるようです。ただし、 はが を呼び出した直後に正しく初期化されます。ソースのメソッドを参照してください。IoC
Bootstrapper
Configure
BootstrapperBase.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.Show
Bootstrapper
MessageBox.Show
エラーをファイルに書き込むように NLog のロギング ルーチンに変更したところ、実際の例外を突き止めることができました。本当の問題は、Autofac が PCL をターゲットにしており、.NET 4.0 Client Profile で問題なく実行できるようにするには、更新プログラム 4.0.3 をターゲット マシンにインストールする必要があることにあります。
しかし、元の問題はさておき、実はカリバーンには問題があります。Bootstrapper の初期化の前に MessageBox.Show を呼び出すと、IoC 構成間で発生するまったく新しいアプリケーション開始プロセスがトリガーされ、観察された例外が生成されるようです。
現在の質問は当初の目的から逸脱していると考えており、終了する必要があると思います。特定のアプリケーションの問題の影響を受けない環境で、Caliburn.Micro の問題を対象とする新しい質問を作成します。