所以这就是你在非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);
仍然没有成功,Reflector将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试图在加载器应用程序中找到.xml而不是在THE ACTUAL wpf应用程序中。
答案 0 :(得分:3)
我找到了一种方法。你基本上有两个选择。
选项首先,虽然更干净,但缺点是速度较慢(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();
}
}
第二种解决方案使用反射来更改当前应用程序的ResourceAssembly。您无法使用公共API执行此操作,因为Application.ResourceAssembly
只有在设置后才会被读取。您必须使用反射来访问Application
和ResourceUriHelper
类的私有成员。
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]);
两种解决方案仍有一个警告:您不能在一个AppDomain中使用多个使用相对资源的Wpf应用程序,因此,如果您希望加载多个应用程序,则需要创建多个AppDomains
。
要使这些示例有效,您需要做两件事:
[STAThread]
答案 1 :(得分:0)
@ghord 的选项 2 非常好,但它需要一种方法来加载 WPF 中引用的 *.dll
。
所以你还得按照以下方式修改WPF的App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
base.OnStartup(e);
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dir = @"C:\your\path\to\WPF\bin\Debug\";
string fileExtension = "*.dll";
string needed = args.Name.Split(',')[0];
if (needed.EndsWith(".resources"))
{
return null;
}
foreach (String file in Directory.GetFiles(dir, fileExtension, SearchOption.TopDirectoryOnly))
{
string name = System.IO.Path.GetFileNameWithoutExtension(file);
if (args.Name.StartsWith(name))
{
byte[] bytes = File.ReadAllBytes(file);
Assembly assembly = Assembly.Load(bytes);
return assembly;
}
}
Debug.WriteLine(args.Name);
return null;
}
}
字符串 dir
可以设置为 WPF 应用程序的 Release
文件夹的路径。
请注意,跳过 .resources
很重要,但对于其余部分,我们可以假设所有需要的 *.dll
文件都位于 dir
文件夹中。
顺便说一句,所有这些都与您的程序集的加密签名配合得很好:例如阅读 my article 以了解如何加密您的 exe 和 dll 文件的签名。
假设您的启动器名为 launcher.exe
,原始 WPF 为 mywfp.exe
,并且您在项目文件夹 TestView
下定义了一个 WPF 窗口 view
。现在您可能会遇到如下异常:
System.Exception:
"The component 'mywfp.TestView' does not have a resource identified by the URI '/mywfp;component/view/testview.xaml'."
这里的快速技巧是:
mywfp.exe
重命名为 mywfp_original.exe
(或您喜欢的名称)launcher.exe
为原始 wpf,即 mywfp.exe
!