WPF程序的两种启动方式

2024-12-29T00:00:00Z | 2分钟阅读 | 更新于 2024-12-29T00:00:00Z

@
WPF程序的两种启动方式

默认启动方式

新建一个名为 WpfApp1 的 WPF 程序,该程序默认包含两个类:

  1. App.xaml
    1. App.cs
  2. MainWindow.xaml
    1. MainWindow.cs

创建完成后,直接按 F5 启动可打开主窗口。那么,此程序是如何启动的呢?

通过 dotPeek 反编译生成的编译文件 WpfApp1.exe,反编译结果如下:

namespace WpfApp1 {
    /// <summary>
    /// App 类
    /// </summary>
    public partial class App : System.Windows.Application {
        /// <summary>
        /// InitializeComponent 方法
        /// </summary>
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "8.0.5.0")]
        public void InitializeComponent() {
            #line 5 "..\..\..\App.xaml"
            this.StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative);
            #line default
            #line hidden
        }
        /// <summary>
        /// 应用程序入口点
        /// </summary>
        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "8.0.5.0")]
        public static void Main() {
            WpfApp1.App app = new WpfApp1.App();
            app.InitializeComponent();
            app.Run();
        }
    }
}

可见编译后生成的 Main 方法,该方法执行了以下操作:

  1. 创建了 App 类型的对象 app
  2. 调用了 app 的 InitializeComponent 方法,从反编译结果可知,该方法中设置了 StartupUri
  3. 调用了 app 的 Run 方法

那么 app.Run 内部究竟做了什么呢?

  1. 检查 UI 线程
  2. this.RunDispatcher((object) null);
public int Run()
{
    EventTrace.EasyTraceEvent((EventTrace.Keyword) 3, (EventTrace.Event) 3);
    return this.Run((Window) null);
}
public int Run(Window window)
{
    this.VerifyAccess();
    return this.RunInternal(window);
}
public void VerifyAccess()
{
    if (this.Thread!= Thread.CurrentThread;)
        throw new InvalidOperationException(MS.Internal.WindowsBase.SR.Get(nameof (VerifyAccess)));
}
internal int RunInternal(Window window)
{
    this.VerifyAccess();
    EventTrace.EasyTraceEvent((EventTrace.Keyword) 3, (EventTrace.Event) 3);
    if (this._appIsShutdown)
        throw new InvalidOperationException(SR.Format(SR.CannotCallRunMultipleTimes, (object) ((object) this).GetType().FullName));
    if (window!= null)
    {
        if (!((DispatcherObject) window).CheckAccess())
            throw new ArgumentException(SR.Format(SR.WindowPassedShouldBeOnApplicationThread, (object) ((object) window).GetType().FullName, (object) ((object) this).GetType().FullName));
        if (!this.WindowsInternal.HasItem(window))
            this.WindowsInternal.Add(window);
        if (this.MainWindow == null)
            this.MainWindow = window;
        if (window.Visibility!= Visibility.Visible)
            this.Dispatcher.BeginInvoke((DispatcherPriority) 10, (Delegate) (obj =>
            {
                (obj as Window).Show();
                return (object) null;
            }), (object) window);
    }
    this.EnsureHwndSource();
    this.RunDispatcher((object) null);
    return this._exitCode;
}

带 Main 函数的方式

新建一个名为 WpfApp2 的 WPF 程序,此程序默认包含两个类:

  1. App.xaml
    1. App.cs
  2. MainWindow.xaml
    1. MainWindow.cs

接着,删除 App.xaml 和 App.cs 类,并添加 Program.cs 文件,添加 Main 函数,如下:

    public class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
            var app = new Application();
            app.StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative);
            app.Run();
        }
    }

其中最为关键的是 STAThread 这个特性,它声明以单线程的方式启动。

总结

这两种方式在本质上实则相同,因为第一种方式在编译完成后,也会转化为第二种写法。相较而言,第二种方式更为灵活,在构造 app 对象之后,能够注册 app 各种生命周期变化的事件,进而开展诸多操作,例如:加载资源文件、确保程序的单例运行、请求接口等等。

Logic will get you from A to B. Imagination will take you everywhere.

© 2026 火箭的博客

🌱 Powered by Hugo with theme Dream.

爱好
  • 三国演义:从大二开始听评书版的《三国演义》,现在主要是作为睡前小故事来听(我估计是第七八遍了)
  • 写代码:没错,写代码也是我的爱好。只要出门超过半天,我基本都会背着笔记本,随时可能进入“编码模式”。
  • 健身:曾经的爱好。结婚之后就逐渐被抛弃了 🤷,结婚一年涨了10斤。
  • 看电影:后续可能会写影评。
  • 探索新事物和工具:准备开一个主题,专门分享我接触到的各种新玩意儿。
关于我

👋 你好,我是 huojian (火箭)

欢迎来到我的个人博客!建立这个小站的初衷,是想在浩瀚的互联网中拥有一块属于自己的“数字花园”。我经常从各类优秀的博客中汲取养分,现在,我也想成为那个输出和分享的人。

这里没有刻板的文章更新 KPI,更多的是我个人的技术沉淀、学习笔记以及生活碎片的真实记录。

👨‍💻 关于我与我的技术栈

我是一名客户端软件工程师,日常与代码和系统架构打交道。在技术探索的道路上,我享受解决复杂问题带来的成就感,也喜欢折腾各种能提升幸福感的工具。

在这里,你可能会看到我分享以下内容:

  • 💻 开发与架构探讨: 探讨 WPF/C# 领域的开发经验、代码质量优化,以及诸如客户端热修复 (Hotfix) 系统设计等我在实际项目中遇到并解决的挑战。
  • 🛠️ 工作流与跨平台折腾: 记录我游走在 Windows 与 macOS 环境下的效率心得。从外设配置到好用的跨平台开发工具,分享如何打造顺手的生产力环境。
  • 🐳 自托管与数字生活: 我是个“自建服务”爱好者。会不定期掉落关于 Docker 部署、云服务器折腾指南,以及如何搭建属于自己的 RSS 阅读环境等教程。
  • 📝 学习笔记与自我精进: 记录各种技术学习心得。同时,我也在持续死磕英语,努力提升口语和写作能力,告别“哑巴英语”,相关的学习路径和思考也会记录在这里。

💡 我的理念

“Talent is enduring patience.” (才能即是长久的忍耐)

我相信持续输出的力量。无论是深度的技术解析,还是一个简单的开发踩坑记录,亦或是一篇普通的生活随笔,都是打造个人 IP 与技术影响力的基石。

📬 建立连接

非常高兴能在这里与同样喜欢写代码、喜欢阅读的你相遇。如果你对我的文章有共鸣,或者想交流技术与生活,欢迎随时找我:

  • GitHub: @huojian-jan
  • RSS 订阅: 欢迎通过博客右上角的 RSS 订阅我的最新文章。
记录什么?
  • 学习笔记

  • 一些想法:记录日常思考,不一定有结论,更像是一种随手的表达。

  • 旅游与随笔:旅行时的见闻与感受,以及偶尔的文字随笔。