编程与数学 03-008 《看潮企业管理软件》项目开发 03 项目结构 3-3

编程与数学 03-008 《看潮企业管理软件》项目开发 03 项目结构 3-3

摘要:本文以《看潮企业管理软件》为例,系统梳理.NET Framework 4.7.2下C# WinForms同步应用的"够用"骨架:从Program入口、MainForm UI、SyncService业务核、Models数据到日志与配置,给出可扩展文件结构、关键代码片段及WinForms同步陷阱------耗时阻塞UI、Refresh/DoEvents喘口气、资源释放、线程安全。随后展示真实ERP层级目录、启动流程、清单与NuGet依赖,兼顾高DPI、长路径、主题、版本重定向,为老框架升级与现代驱动兼容提供一站式参考。
关键词:WinForms同步、.NET4.7.2、看潮ERP、SyncService、高DPI、Npgsql、DevExpress、应用清单、NuGet、项目结构
人工智能助手:DeepSeek、Kimi


二、项目结构(实际)

(四)应用程序入口

csharp 复制代码
// Program.cs
// 引入必要的命名空间
// 看潮ERP主程序
using DevExpress.XtraBars;          // DevExpress 工具栏控件
using DevExpress.XtraWaitForm;      // DevExpress 等待窗体
using System;
using System.Drawing;
using System.Windows.Forms;
using static KcErp.KcMain;          // 静态引入 KcMain 类,以便直接访问其成员

namespace KcErp
{
    internal static class Program
    {
        // 指定程序的线程模型为单线程单元(STA),这对于Windows窗体应用程序是必需的
        [STAThread]
        private static void Main()
        {
            // 启用应用程序的视觉样式,让控件使用操作系统主题
            Application.EnableVisualStyles();
            // 设置控件使用新的文本渲染方式(GDI+),以获得更好的文本显示效果
            Application.SetCompatibleTextRenderingDefault(false);
            // 设置视觉样式同时应用于客户区和非客户区(如窗口边框)
            Application.VisualStyleState = System.Windows.Forms.VisualStyles.VisualStyleState.ClientAndNonClientAreasEnabled;

            // 订阅DevExpress外观风格改变事件,确保当风格改变时,默认字体始终为"微软雅黑 12号"
            DevExpress.LookAndFeel.UserLookAndFeel.Default.StyleChanged += (s, e) =>
            {
                DevExpress.Utils.AppearanceObject.DefaultFont = new System.Drawing.Font("微软雅黑", 12F);
            };

            // 设置当前线程的UI区域文化为中文(中国),影响日期、货币等格式化方式,并使界面本地化生效
            System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN");

            // 注册DevExpress的额外皮肤,让应用程序可以使用更多皮肤选项
            DevExpress.UserSkins.BonusSkins.Register();

            // 将应用程序的启动路径(exe所在目录)赋值给KcMain类中的全局变量pAppPath,供其他地方使用
            pAppPath = AppDomain.CurrentDomain.BaseDirectory;
            // 调用KcMain类中的方法,加载ERP系统核心配置
            LoadX9set();

            // 创建登录窗体实例,使用using确保窗体资源会被正确释放
            using (var loginForm = new FmLogin())
            {
                // 以模态对话框形式显示登录窗体,程序将在此等待登录操作完成
                loginForm.ShowDialog();

                // 根据KcDb类的SFlj属性判断是否成功连接数据库(或登录成功)
                if (KcDb.SFlj)
                {
                    // 如果连接成功,启动主消息循环并运行主窗体(FmMain),程序进入主界面
                    Application.Run(new FmMain());
                }
                else
                {
                    // 如果连接失败,退出应用程序
                    Application.Exit();
                }
            } // 登录窗体在此处自动释放
        }
    }
}

代码核心流程解析

这段代码清晰地展示了ERP应用程序的标准启动三步曲

  1. 初始化配置:设置UI风格、文化、皮肤和加载系统设置。
  2. 身份验证 :通过登录窗体 (FmLogin) 验证用户身份。
  3. 运行主体 :验证通过后,启动主窗体 (FmMain) 进入系统;否则退出。

关键技术与设计模式

  • STAThread属性:Windows窗体程序的强制要求。
  • using语句 :自动资源管理(IDisposable模式),确保FmLogin窗体资源被释放。
  • 事件订阅:动态保持DevExpress控件的字体一致性。
  • 配置与状态分离LoadX9set()加载配置,KcDb.SFlj判断数据库连接状态,职责清晰。

(五)应用配置文件

App.config

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System">
      <section name="DevExpress.LookAndFeel.Design.AppSettings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <DevExpress.LookAndFeel.Design.AppSettings>
      <setting name="DPIAwarenessMode" serializeAs="String">
        <value>System</value>
      </setting>
      <setting name="RegisterBonusSkins" serializeAs="String">
        <value>True</value>
      </setting>
    </DevExpress.LookAndFeel.Design.AppSettings>
  </applicationSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

这是一个典型的 .NET Framework 应用程序的 App.config 文件,作用是给 CLR(运行时)和 DevExpress 皮肤组件提供启动与兼容指令。逐段说明如下:

  1. 文件头
    <?xml version="1.0" encoding="utf-8"?>

    标准 XML 声明,告诉解析器用 UTF-8 编码读取。

  2. 配置节声明

    xml 复制代码
    <configSections>
      <sectionGroup name="applicationSettings" ...>
        <section name="DevExpress.LookAndFeel.Design.AppSettings" .../>
      </sectionGroup>
    </configSections>

    先"注册"后面要用到的配置节。

    • applicationSettings 是 .NET 自带的组别,用于存放强类型应用程序设置。
    • 这里额外注册了一个子节 DevExpress.LookAndFeel.Design.AppSettings,以便 DevExpress 控件能把自己的皮肤/DPI 相关设置写进配置文件。
  3. 应用程序设置(DevExpress 皮肤与 DPI)

    xml 复制代码
    <applicationSettings>
      <DevExpress.LookAndFeel.Design.AppSettings>
        <setting name="DPIAwarenessMode" serializeAs="String">
          <value>System</value>
        </setting>
        <setting name="RegisterBonusSkins" serializeAs="String">
          <value>True</value>
        </setting>
      </DevExpress.LookAndFeel.Design.AppSettings>
    </applicationSettings>
    • DPIAwarenessMode=System
      让 DevExpress 控件跟随系统的 DPI 缩放模式(Per-Monitor V2 或 System-Aware),避免在高 DPI 屏上模糊。
    • RegisterBonusSkins=True
      启动时自动把 DevExpress 赠送的"Bonus 皮肤包"注册到程序里,使界面能选用更多外观主题。
  4. 运行时版本锁定

    xml 复制代码
    <startup>
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>

    告诉 CLR:本程序只能跑在 .NET Framework 4.7.2 及以上版本,低于此版本会直接拒绝启动。

  5. 程序集重定向(bindingRedirect)

    xml 复制代码
    <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        ...
      </assemblyBinding>
    </runtime>

    当 NuGet 包或 DevExpress 套件引用的"系统程序集"版本与 GAC 里已安装的不一致时,CLR 按这里的规则把旧版本请求转发到新版本,避免"找不到程序集"异常。

    例:
    System.Numerics.Vectors 任何 0.0.0.0--4.1.4.0 区间的版本请求,都会被重定向到 4.1.4.0。

    其余 System.Runtime.CompilerServices.UnsafeSystem.BuffersSystem.Threading.Tasks.ExtensionsSystem.ValueTuple 同理。

总结:这份配置文件的核心职责只有三件:

  1. 让 DevExpress 控件在高 DPI 屏上正常缩放并加载额外皮肤;
  2. 强制程序运行在 .NET 4.7.2 运行时;
  3. 统一解决系统程序集版本冲突,保证启动不报错。

(六)应用程序清单文件

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC 清单选项
             如果想要更改 Windows 用户帐户控制级别,请使用
             以下节点之一替换 requestedExecutionLevel 节点。
        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
            如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
            元素。
        -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
           Windows 版本的列表。取消评论适当的元素,
           Windows 将自动选择最兼容的环境。 -->
      <!-- Windows Vista -->
      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
      <!-- Windows 7 -->
      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
      <!-- Windows 8 -->
      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
      <!-- Windows 8.1 -->
      <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
      <!-- Windows 10 -->
      <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
    </application>
  </compatibility>
  <!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
       选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
       在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
       将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
  <!--<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
		<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    </windowsSettings>
  </application>-->
  <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
  <!--
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>
  -->
</assembly>

这是一份Windows 应用程序清单文件 (Application Manifest,通常以 *.exe.manifest 或嵌入到 app.manifest 的形式存在)。

它的作用是在程序启动前告诉 Windows:

"我是谁、我需要什么权限、我兼容哪些系统版本、我要不要高 DPI 支持、要不要 XP 风格控件......"

下面逐段翻译并说明含义。

  1. 文件头

    xml 复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">

    标准 XML 声明 + 清单根节点。manifestVersion="1.0" 固定写法。

  2. 程序身份

    xml 复制代码
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>

    给当前可执行文件起个"内部名字"和版本号,仅作标识,不影响实际文件名。

  3. UAC 权限请求(trustInfo)

    xml 复制代码
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
      <security>
        <requestedPrivileges>
          <requestedExecutionLevel level="asInvoker" uiAccess="false" />
        </requestedPrivileges>
      </security>
    </trustInfo>
    • level="asInvoker"
      程序需要管理员权限,用户当前是什么权限就什么权限运行(最常用、最安全)。
    • uiAccess="false"
      程序 需要给更高权限的窗口发送消息(如屏幕键盘、辅助技术才需要设为 true)。
      注释里还列出了其它选项:
      requireAdministrator → 强制弹 UAC 提权;
      highestAvailable → 能提就提,不能提就普通权限。
  4. 操作系统兼容性声明(compatibility)

    xml 复制代码
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
      <application>
        <!-- 所有 supportedOS 节点都被注释掉了 -->
      </application>
    </compatibility>

    如果取消注释某个 supportedOS GUID,就等于告诉 Windows

    "我专门为这个版本做过测试,请别对我启用兼容层"。

    示例 GUID:

    • {e2011457-1546-43c5-a5fe-008deee3d3f0} → Windows Vista
    • {8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a} → Windows 10
      全部注释掉时,Windows 会按"最老兼容模式"对待程序,可能启用旧版文件/注册表虚拟化。
  5. DPI 与长路径感知(被注释掉)

    xml 复制代码
    <!--
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
        <dpiAware>true</dpiAware>
        <dpiAwareness>PerMonitorV2</dpiAwareness>
        <longPathAware>true</longPathAware>
      </windowsSettings>
    </application>
    -->

    若取消注释:

    • dpiAware + dpiAwareness=PerMonitorV2
      程序自己处理高 DPI,Windows 不会对它做位图拉伸;WPF 默认已感知,WinForms 需 4.7+ 并在 app.config 里再加 EnableWindowsFormsHighDpiAutoResizing=true
    • longPathAware=true
      允许程序使用超过 260 字符的 NT 长路径(需同时满足:系统组策略打开 + 目标系统 Win10 1607+ + 路径前加 \\?\ 或使用新 API)。
  6. XP 视觉样式(被注释掉)

    xml 复制代码
    <!--
    <dependency>
      <dependentAssembly>
        <assemblyIdentity type="win32"
                          name="Microsoft.Windows.Common-Controls"
                          version="6.0.0.0"
                          processorArchitecture="*"
                          publicKeyToken="6595b64144ccf1df"
                          language="*" />
      </dependentAssembly>
    </dependency>
    -->

    取消注释后,程序里的 Win32 控件(按钮、列表、进度条等)会启用 XP 及以后系统提供的"主题样式",而不是老旧的 95 风格。

    WinForms/WPF 默认会自动嵌入这段,所以通常不需要手动打开。

一句话总结

这份清单目前几乎没启用任何高级特性

  • 普通权限运行(asInvoker)
  • 未声明兼容任何新系统 → 可能运行在兼容模式
  • DPI、长路径、主题样式全部注释掉 → 依赖系统默认行为

如果想让程序在高 DPI 屏不模糊、支持长路径、界面更好看,就把对应节点取消注释并重新编译即可。

(七)NuGet 依赖清单

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.0" targetFramework="net472" />
  <package id="Npgsql" version="4.1.14" targetFramework="net472" />
  <package id="System.Buffers" version="4.5.0" targetFramework="net472" />
  <package id="System.Memory" version="4.5.3" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net472" />
  <package id="System.Text.Encodings.Web" version="4.6.0" targetFramework="net472" />
  <package id="System.Text.Json" version="4.6.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.3" targetFramework="net472" />
  <package id="System.ValueTuple" version="4.5.0" targetFramework="net472" />
</packages>

这是 packages.config ------ .NET Framework 项目用来"记账"的 NuGet 依赖清单。

每一行 <package> 记录了一个 NuGet 包在安装时的精确坐标,供 Visual Studio / MSBuild 还原用。

字段含义

  • id:包名(NuGet 画廊里的唯一标识)。
  • version:安装时锁定的版本号,还原时必须完全匹配。
  • targetFramework="net472":当时项目的目标框架,决定 NuGet 应该拉取哪一组 dll(net472 文件夹下的)。

逐行速览

  1. Microsoft.Bcl.AsyncInterfaces 1.1.0

    给旧版 .NET Framework 提供 IAsyncEnumerable<T>IAsyncDisposable 等接口,使项目能在 4.7.2 上使用新版异步枚举器。

  2. Npgsql 4.1.14

    PostgreSQL 官方 .NET 驱动;4.1.14 是 2021 年前后的稳定版,支持 .NET Standard 2.0,因此能在 net472 运行。

  3. System.Buffers 4.5.0

    提供 ArrayPool<T> 等缓冲池基础类型,减少 GC 压力;被 Npgsql、System.Text.Json 等内部依赖。

  4. System.Memory 4.5.3

    引入 Span<T>Memory<T>ReadOnlySequence<T> 等高性能内存抽象;net472 本身没有这些类型,需要外置包。

  5. System.Numerics.Vectors 4.5.0

    提供硬件加速的 2D/3D/4D 向量类型(Vector2, Vector3, Vector<T>),依赖 SIMD 指令集。

  6. System.Runtime.CompilerServices.Unsafe 4.6.0

    官方"不安全"小工具包,封装 Unsafe.AddUnsafe.As 等指针操作,被高性能库广泛引用。

  7. System.Text.Encodings.Web 4.6.0

    提供 HTML/JavaScript/URL 字符转义器,是 System.Text.Json 的底层依赖。

  8. System.Text.Json 4.6.0

    微软新一代高性能 JSON 序列化库;4.6.0 是 2019 年底发布,能在 net472 运行(基于 .NET Standard 2.0)。

  9. System.Threading.Tasks.Extensions 4.5.3

    补充 ValueTask<T>IValueTaskSource 等类型,让异步方法减少堆分配。

  10. System.ValueTuple 4.5.0

    给 net472 带来 C# 7 元组语法糖 (int x, string y),否则编译器找不到 ValueTuple<...> 类型。

一句话总结

这份清单把 Npgsql + System.Text.Json 两大功能库以及它们所需的"现代 .NET Core 时代"基础类型(Span、ValueTask、ValueTuple、Buffers、Unsafe...)全部拉回 .NET Framework 4.7.2,使老项目也能用上高性能 JSON 与最新 PostgreSQL 驱动,同时通过精确版本锁定保证团队还原出完全一致的依赖树。

全文总结

文章先提出"完全同步WinForms亦需防假死"的最小骨架,用Program-MainForm-SyncService-Models-Config五件套示范如何拆分职责、拆解耗时步骤、借Refresh保持响应,并强调using释放资源与线程安全。继而给出真实项目《看潮ERP》的完整目录:01Main主窗体、02Designer字典、03Common工具、05Users功能面板、11Forms业务、21Report报表、31Data数据访问,bin里DevExpress与Npgsql共存,展示企业级复杂度。Program.cs中STA、皮肤、文化、路径、配置加载、登录验证、主窗体启动六步一目了然;App.config通过bindingRedirect解决System.*版本冲突,并启用DPI与BonusSkins;app.manifest注释掉高DPI、长路径、主题选项,解释何时取消注释;packages.config把System.Text.Json、Span、ValueTask等新API拉回.NET4.7.2,兼顾高性能与旧框架兼容。通篇兼顾"能跑"与"能扩展",为仍在维护WinForms老系统的开发者提供可直接套用的结构模板、配置样例与避坑指南,也为后续异步改造、云原生迁移奠定清晰边界。

(本课题全文完)

相关推荐
明月看潮生1 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 02 技术选型 3-1
erp·企业开发·技术选型·编程与数学·.net开发·c#编程·项目实践、
明月看潮生2 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 01 需求分析 3-1
c#·.net·需求分析·erp·企业开发·项目实践·编程与数学
明月看潮生2 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 01 需求分析 3-2
需求分析·erp·企业开发·项目实践·编程与数学·.net开发·c#编程
kuankeTech3 天前
解决内外贸双轨制难题,外贸ERP智能引擎同步管理国内外合规与标准
大数据·人工智能·数据可视化·软件开发·erp
沛沛老爹9 天前
Web转AI架构篇 Agent Skills vs MCP:工具箱与标准接口的本质区别
java·开发语言·前端·人工智能·架构·企业开发
kuankeTech9 天前
从“数据孤岛”到“价值金矿”:外贸ERP如何激活传统制造企业“沉睡数据”?
大数据·人工智能·制造·软件开发·erp
kuankeTech10 天前
从经验驱动到数据驱动:外贸ERP打通大宗矿业企业管理“任督二脉”
大数据·人工智能·经验分享·软件开发·erp
沛沛老爹11 天前
Web开发者突围AI战场:Agent Skills元工具性能优化实战指南——像优化Spring Boot一样提升AI吞吐量
java·开发语言·人工智能·spring boot·性能优化·架构·企业开发
Vicky-Min14 天前
NetSuite Credit Memo导入的基础CSV模板
oracle·erp