从内存加载WPF应用程序 [英] Load WPF application from the memory

查看:91
本文介绍了从内存加载WPF应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,这是在非WPF应用程序中调用Main()的方式:

So this is how you invoke Main() in non WPF applications:

var entry = assembly.EntryPoint;

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

但是我试过了(对于MSDN来说,它根本不适用于WPF应用程序) :

but somehow it doesn't work at all for WPF applications, I've tried (MSDN's way):

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);

仍然没有成功,Reflector显示Main()为

still no success, Reflector shows Main() as

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

无论我做什么,都会收到 System.Reflection.TargetInvocationException异常

no matter what I do, I get "System.Reflection.TargetInvocationException" exception.

有什么帮助吗?

PS。更多调试显示它无法加载最初位于我要加载的程序集中的 mainwindow.xaml资源

PS. More debugging revealed it can't load „mainwindow.xaml" resource originally located in the assembly that I want to load

{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尝试在加载程序应用程序中而不是在ACTUAL WPF应用程序中查找.xml。

so I suspect, the problem is that CLR tries to find .xml within loader application and not within THE ACTUAL wpf application.

推荐答案

我找到了一种方法。您基本上有两个选择。

I've found a way to do that. You basically have two options.


  1. 将exe加载到单独的AppDomain中。

  2. 使用反射技巧来更改默认的ResourceAssembly。 / li>
  1. Load the exe in the separate AppDomain.
  2. Use reflection trickery to change default ResourceAssembly.

选项虽然较干净,但缺点是速度较慢(WPF也需要加载到新的AppDomain中):

Option first, while cleaner, has disadvantage of being slower (WPF needs to load into the new AppDomain too):

//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();
    }
}

第二种解决方案使用反射来更改当前应用程序的ResourceAssembly 。您不能使用公共API来执行此操作,因为 Application.ResourceAssembly 设置为只读。您必须使用反射来访问 Application ResourceUriHelper 类的私有成员。

The second solution uses reflection to change ResourceAssembly of current application. You cannot do that with public API's, as the Application.ResourceAssembly is read only once it's set. You have to use reflection to access private members of both Application and ResourceUriHelper classes.

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]);

两种解决方案仍需注意:您不能使用多个使用相对资源的Wpf应用程序在一个AppDomain中,因此,如果您希望加载多个应用程序,则需要创建多个 AppDomains

There is still a caveat with both solutions: You cannot use more than one Wpf application which uses relative resources in one AppDomain, so should you wish to load more than one app, you need to create multiple AppDomains.

为了使这些示例正常工作,您需要做两件事:

For those examples to work, you need to do two things:


  • 它需要从单个单元状态线程中调用,因此请记住使用 [STAThread]

  • 您需要添加PresentationCore.dll,PresentationFramework.dll和WindowsBase.dll引用

这篇关于从内存加载WPF应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆