4

したがって、これは非 WPF アプリケーションで Main() を呼び出す方法です。

var entry = assembly.EntryPoint;

if (assembly.EntryPoint.GetParameters().Length == 0)
    entry.Invoke(null, new object[0]);
else
    entry.Invoke(null, new object[] { args });

しかし、どういうわけか、WPFアプリケーションではまったく機能しません。試してみました(MSDNの方法):

Assembly asm = Assembly.LoadFrom(file);

Type myType = asm.GetType("WpfApplication1.App");

// Get the method to call.
MethodInfo myMethod = myType.GetMethod("Main");

// Create an instance.
object obj = Activator.CreateInstance(myType);

// Execute the method.
myMethod.Invoke(obj, null);

それでも成功しない場合、リフレクターは Main() を次のように表示します

[DebuggerNonUserCode, STAThread]
public static void Main()
{
    App app = new App();
    app.InitializeComponent();
    app.Run();
}

何をしても、「System.Reflection.TargetInvocationException」例外が発生します。

何か助けはありますか?

PS。さらにデバッグすると、読み込みたいアセンブリに元々ある「mainwindow.xaml」リソースを読み込めないことが明らかになりました

{System.IO.IOException: Nie można zlokalizować zasobu „mainwindow.xaml”.
   w MS.Internal.AppModel.ResourcePart.GetStreamCore(FileMode mode, FileAccess access)
   w System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess access)
   w System.IO.Packaging.PackagePart.GetStream()
   w System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean bSkipJournaledProperties)
   w System.Windows.Application.DoStartup()
   w System.Windows.Application.<.ctor>b__0(Object unused)
   w System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   w System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   w System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   w System.Windows.Threading.DispatcherOperation.InvokeImpl()
   w System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   w System.Threading.ExecutionContext.runTryCode(Object userData)
   w System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   w System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   w System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   w System.Windows.Threading.DispatcherOperation.Invoke()
   w System.Windows.Threading.Dispatcher.ProcessQueue()
   w System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   w MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   w MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   w System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   w System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   w System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   w System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)
   w System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   w MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   w MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   w System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   w System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   w System.Windows.Threading.Dispatcher.Run()
   w System.Windows.Application.RunDispatcher(Object ignore)
   w System.Windows.Application.RunInternal(Window window)
   w System.Windows.Application.Run(Window window)
   w System.Windows.Application.Run()
   w WpfApplication1.App.Main()}

したがって、問題は、CLRが実際のwpfアプリケーション内ではなく、ローダーアプリケーション内で.xmlを見つけようとすることです。

4

2 に答える 2

4

私はそれを行う方法を見つけました。基本的に 2 つのオプションがあります。

  1. 別の AppDomain に exe をロードします。
  2. リフレクション トリックを使用して、デフォルトの ResourceAssembly を変更します。

最初のオプションは、よりクリーンですが、遅くなるという欠点があります (WPF も新しい AppDomain にロードする必要があります)。

//Assembly: WpfLoader.dll
[STAThread]
class Program
{
    public class Loader : MarshalByRefObject
    {
        public void Load()
        {
            var dll = File.ReadAllBytes("WpfTest.exe");
            var assembly = Assembly.Load(dll);
            Application.ResourceAssembly = assembly;
            assembly.EntryPoint.Invoke(null, new object[0]);
        }


    }


    static void Main(string[] args)
    {
        var domain = AppDomain.CreateDomain("test");
        domain.Load("WpfLoader");

        var loader = (Loader)domain.CreateInstanceAndUnwrap("WpfLoader", "WpfLoader.Program+Loader");
        loader.Load();
    }
}

2 番目のソリューションは、リフレクションを使用して現在のアプリケーションの ResourceAssembly を変更します。Application.ResourceAssemblyは一度設定すると読み取り専用になるため、パブリック API ではこれを行うことはできません。ApplicationResourceUriHelperクラスの両方のプライベート メンバーにアクセスするには、リフレクションを使用する必要があります。

var dll = File.ReadAllBytes("WpfTest.exe");
var assembly = Assembly.Load(dll);

var app = typeof(Application);

var field = app.GetField("_resourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, assembly);

//fix urihelper
var helper = typeof(BaseUriHelper);
var property = helper.GetProperty("ResourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
property.SetValue(null, assembly, null);

//load
assembly.EntryPoint.Invoke(null, new object[0]);

両方のソリューションにはまだ注意事項があります。1 つの AppDomain で相対リソースを使用する複数の Wpf アプリケーションを使用することはできません。したがって、複数のアプリをロードする場合は、複数の を作成する必要がありますAppDomains

これらの例を機能させるには、次の 2 つのことを行う必要があります。

  • 単一のアパートメント ステート スレッドから呼び出す必要があるため、忘れずに使用してください[STAThread]
  • PresentationCore.dll、PresentationFramework.dll、および WindowsBase.dll 参照を追加する必要があります。
于 2013-06-27T07:58:42.830 に答える