替换 WPF 入口点

Posted

技术标签:

【中文标题】替换 WPF 入口点【英文标题】:Replacing the WPF entry point 【发布时间】:2011-09-03 15:13:33 【问题描述】:

WPF 定义了自己的Main() 方法。我应该如何用我自己的 Main 方法替换它,该方法(通常)打开 WPF MainWindow(例如,通过命令行参数添加非 WPF 脚本模式)?

【问题讨论】:

【参考方案1】:

一些示例描述了将 App.xaml 的构建操作从 ApplicationDefinition 更改为 Page 并编写自己的 Main() 来实例化 App 类并调用其 Run() 方法,但这可能会在App.xaml 中应用程序范围资源的解析。

相反,我建议在自己的类中创建自己的Main(),并在项目属性中将启动对象设置为该类:

public class EntryPoint 
    [STAThread]
    public static void Main(string[] args) 
        if (args != null && args.Length > 0) 
            // ...
         else 
            var app = new App();
            app.InitializeComponent();
            app.Run();
        
    

我这样做是为了利用一些AppDomain 事件,这些事件必须在其他任何事情发生之前订阅(例如AssemblyResolve)。我遇到的将 App.xaml 设置为 Page 的不良后果包括我的 UserControl 视图 (M-V-VM) 在设计时无法解析 App.xaml 中保存的资源。

【讨论】:

好的,我正在调用 App.Main() 而不是 Run(),因为 Main() 调用 InitializeComponent(),它会安装 Startup 事件处理程序。我猜如果您将 Build Action 更改为 Page(因为 Main() 消失),您必须调用 Run() 但我只是将其保留为 ApplicationDefinition。 生成的Main()只是实例化App并调用Run()Startup 事件在System.Windows.Application 的构造函数中触发。 Run() 附加 Dispatcher 并开始消息泵。 InitializeComponent() 应该在 Apps 的构造函数中调用。不是吗? 我将构造函数添加到App 并在那里调用InitializeComponent() 以避免App.Main()。为简洁起见,其余部分我过于简化了。 StartupApplication.OnStartup() 触发,只有派生的App 类的构造函数可以在触发之前订阅Startup。这是因为Application 的构造函数异步调用了一个调用OnStartup() 的方法,所以它实际上是在基构造函数和派生构造函数完成后运行的。 不必解析定义的“主要”入口点内的命令行参数,是否有某种方法可以将这些参数传递给方法中定义的 WPF 应用程序实例,以便它们可以由定义的“启动”(或 OnStartup)覆盖(通过 e.Args 属性)处理? 不用移动(或复制)App.Main(),您只需添加它并将其设置为项目的入口点,然后直接调用App.Main()【参考方案2】:

通常我会编辑 App.xaml 以添加此支持:

<Application x:Class="SomeNamespace.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Startup="Application_Startup">

相关部分是我从StartupUri 更改为Startup,并在App.xaml.cs 中使用了一个事件处理程序。这是一个例子:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application

    private void Application_Startup(object sender, StartupEventArgs e)
    
        int verbose = 0;
        var optionSet = new OptionSet
        
             "v|verbose", "verbose output, repeat for more verbosity.",   
                    arg => verbose++ 
        ;

        var extra = optionSet.Parse(e.Args);
        var mainWindow = new MainWindow(verbose);
        mainWindow.Show();
    

【讨论】:

尽管使用这种方法,除非您从命令窗口运行它,否则您将看不到任何Console.* 输出。 这种方法让我可以将构造函数参数传递给主窗口,这很好。我什至可以将它与 Joel 的方法结合起来。 感谢您指出它是“Startup”而不是“StartupUri”!【参考方案3】:

伙计们 问题是你的程序有两个静态 Main() 方法,这会导致编译器抱怨;要解决此问题,请尝试以下方法之一:

告诉编译器您的静态 Main() 方法应该是执行入口点 — 将项目的“启动对象”设置设置为包含您的静态 Main() 方法的类(在解决方案资源管理器中右键单击项目,选择“属性”,然后在“应用程序”选项卡下查找“启动对象”设置)。 关闭自动生成 App.g.cs 的静态 Main() 方法——在解决方案资源管理器中,右键单击 App.xaml,选择“属性”,然后将“构建操作”从“应用程序定义”更改为“页面” ”。

【讨论】:

谢谢;第二个要点至关重要 - 巧妙地放在那里!【参考方案4】:

使用您的自定义静态 Main 方法创建新类。在这个方法的最后,只需调用 WPF 生成的原始 App.Main():

public class Program

    [STAThread]
    public static void Main(string[] args)
    
        // Your initialization code
        App.Main();
    

然后将项目的“启动对象”设置为包含静态 Main() 的类。

【讨论】:

【参考方案5】:

使用自定义 Main() 可能会遇到问题,因为未设置 StartupUri。

您可以使用它在您的 App 类中轻松设置它(不要忘记从 App.xaml 中删除 StartupUri 并将其 Build Action 设置为 Page):

[STAThread]
static void Main()

    App app = new App();
    app.InitializeComponent();
    app.Run();


protected void OnStartup(object sender, StartupEventArgs e)

        var toUri = new UriTypeConverter();
        StartupUri = (Uri)toUri.ConvertFrom("MainWindow.xaml");
...

【讨论】:

以上是关于替换 WPF 入口点的主要内容,如果未能解决你的问题,请参考以下文章

DLL 的入口点

Visual Studio MSIX 应用创建:入口点 (.exe) 不正确

“使用 /main 编译以指定包含入口点的类型。”

C#提示不只定义了一个入口点,请使用/main进行编译以指定包含入口点的类型

为啥 `[DllImport]` 会以 `RtlSecureZeroMemory` 的入口点失败,即使它是一个有据可查的入口点?

由于缺少入口点,场景无法访问