C# 自更新WPF应用程序,启动器进程通过反射加载WPF应用程序

C# 自更新WPF应用程序,启动器进程通过反射加载WPF应用程序,c#,wpf,launcher,C#,Wpf,Launcher,在Winforms应用程序中,我们开发了一个自我更新的应用程序启动器,我正试图用WPF模仿它,但我面临一些问题。使用Winforms时的工作方式: 启动程序进程(不参考主应用程序)将检查更新的库,并根据需要下载 然后,Launcher将从STA线程加载程序集(assembly.load),然后通过反射调用该程序集中的Init方法(当Launcher充当启动屏幕并显示进度时,该方法执行一系列Init逻辑) 初始化完成后,Laucher将通过加载程序集中的反射调用一个切换方法,该方法将使用新的M

在Winforms应用程序中,我们开发了一个自我更新的应用程序启动器,我正试图用WPF模仿它,但我面临一些问题。使用Winforms时的工作方式:

  • 启动程序进程(不参考主应用程序)将检查更新的库,并根据需要下载
  • 然后,Launcher将从STA线程加载程序集(assembly.load),然后通过反射调用该程序集中的Init方法(当Launcher充当启动屏幕并显示进度时,该方法执行一系列Init逻辑)
    • 初始化完成后,Laucher将通过加载程序集中的反射调用一个切换方法,该方法将使用新的MainForm创建一个新的ApplicationContext,然后调用Application.Run(newAppContext)
    • 启动器将关闭其窗口
我试图在WPF中模仿同样的方法,但我遇到的问题是:

  • 似乎没有ApplicationContext的概念,我可以将MainForm传输到它
  • 我不确定如何处理App.xaml/resources,因为我无法在应用程序dll中放置“第二个”App.xaml,所以我不确定如何处理资源/样式的加载
  • 当我在通过反射调用的“切换”呼叫中显示新的主窗口时,该窗口会短暂打开,但随后消失

非常感谢关于如何在Wpf应用程序中实现所需行为的任何指导。

Wpf应用程序源自System.Windows.application的基类。VS使用的基本模板创建App.xaml和App.xaml.cs。此类的静态入口点为

public static void Main()
这就是我使用的:

var type = yourloadedassembly.GetType( "YourNamespace.App" );
type.InvokeMember( "Main", BindingFlags::Public | BindingFlags::Static | BindingFlags::InvokeMethod, null, null, null );

对于任何感兴趣的人来说,解决这一问题实际上都是微不足道的:

  • 创建Wpf应用程序(启动器)
  • 创建承载应用程序特定代码入口点的“应用程序”程序集(可以是dll)
  • 让启动器动态加载应用程序集(assembly.load)
  • 在应用程序集中,有一些静态入口点,可以通过启动器的反射来调用
  • 当启动器通过反射调用入口点方法时,从应用程序集中添加资源和新的MainWindow以分配给application.Current.MainWindow:

       Application.Current.Resources = new ResourceDictionary() {Source = new Uri("pack://application:,,,/MyApp.UI.Styling;component/Common.xaml")};
       Application.Current.MainWindow = new MainWindow();
       Application.Current.MainWindow.Show();
    
  • 回到启动器中,关闭()启动器窗口


为什么不使用ClickOnce?工作完成了。在任何情况下,有没有代码要显示?除了ClickOne,我以前做过的比你建议的更简单的方法就是制作一个单独的应用程序进行更新。让你的应用程序检查,
处理。启动你的更新程序,关闭应用程序,更新程序执行操作,然后重新启动你的应用程序。这相当简单,直截了当,并且可以避免无法覆盖正在运行/正在使用的.System.Windows.Application.Run(mainWindow)的.exe或.dll,这是一种实例方法,我要问,为什么不将其作为一个普通的可执行程序启动。启动并为其提供切换命令行参数?您的另一个选项是使用AppDomain ExecuteAssembly函数之一。@Joellucy请参见:Launcher将从STA线程加载程序集(assembly.load),然后通过反射调用该程序集中的Init方法(当Launcher充当启动屏幕并显示进度时,该方法执行一系列Init逻辑)-启动器在初始化时查询加载的程序集并接收状态更新,因此Process.Start在我尝试获取以下信息时无效:InvalidOperationException:无法在同一AppDomain中创建多个System.Windows.Application实例。您的更新程序是WPF应用程序吗?如果是这样,您可能需要从单独的AppDomain运行它。WPF喜欢在自己的空间中。是的,是的-我正在尝试,但仍然得到相同的错误,不确定我做错了什么:var-domain=AppDomain.CreateDomain(“test”,null,new-AppDomainSetup{ApplicationBase=Environment.CurrentDirectory});var asm=domain.Load(“MyApp”);var types=asm.GetTypes();…获取我的类型.InvokeMember(“Main”,BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,null,null);如果我试图直接调用AppDomain:domain.DoCallBack(()=>type.InvokeMember(“Main”),BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,null,null,null));我得到:System.Runtime.Serialization.SerializationException:“在程序集”启动器中键入“StartUpManager+c__DisplayClass9_0”,版本=1.0.0.0,区域性=中性,PublicKeyToken=null”未标记为可序列化。“AppDomains非常挑剔。”。很容易将程序集加载到您自己的域中。我怀疑,当您通过asm.GetTypes()获取类型时,您实际上是在将该程序集加载到当前域中,并且它正试图从该域运行该程序集。我在AppDomains方面缺乏专业知识。我知道您调用的任何类都需要可序列化才能跨越域边界,我使用CreateInstanceFromAndUnwrap跨越该类并调用其上的函数。