编程与数学 03-008 《看潮企业管理软件》项目开发 03 项目结构 3-3
- 二、项目结构(实际)
-
- (四)应用程序入口
- (五)应用配置文件
- (六)应用程序清单文件
- [(七)NuGet 依赖清单](#(七)NuGet 依赖清单)
- 全文总结
摘要:本文以《看潮企业管理软件》为例,系统梳理.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应用程序的标准启动三步曲:
- 初始化配置:设置UI风格、文化、皮肤和加载系统设置。
- 身份验证 :通过登录窗体 (
FmLogin) 验证用户身份。 - 运行主体 :验证通过后,启动主窗体 (
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 皮肤组件提供启动与兼容指令。逐段说明如下:
-
文件头
<?xml version="1.0" encoding="utf-8"?>标准 XML 声明,告诉解析器用 UTF-8 编码读取。
-
配置节声明
xml<configSections> <sectionGroup name="applicationSettings" ...> <section name="DevExpress.LookAndFeel.Design.AppSettings" .../> </sectionGroup> </configSections>先"注册"后面要用到的配置节。
applicationSettings是 .NET 自带的组别,用于存放强类型应用程序设置。- 这里额外注册了一个子节
DevExpress.LookAndFeel.Design.AppSettings,以便 DevExpress 控件能把自己的皮肤/DPI 相关设置写进配置文件。
-
应用程序设置(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 皮肤包"注册到程序里,使界面能选用更多外观主题。
-
运行时版本锁定
xml<startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup>告诉 CLR:本程序只能跑在 .NET Framework 4.7.2 及以上版本,低于此版本会直接拒绝启动。
-
程序集重定向(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.Unsafe、System.Buffers、System.Threading.Tasks.Extensions、System.ValueTuple同理。
总结:这份配置文件的核心职责只有三件:
- 让 DevExpress 控件在高 DPI 屏上正常缩放并加载额外皮肤;
- 强制程序运行在 .NET 4.7.2 运行时;
- 统一解决系统程序集版本冲突,保证启动不报错。
(六)应用程序清单文件
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 风格控件......"
下面逐段翻译并说明含义。
-
文件头
xml<?xml version="1.0" encoding="utf-8"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">标准 XML 声明 + 清单根节点。
manifestVersion="1.0"固定写法。 -
程序身份
xml<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>给当前可执行文件起个"内部名字"和版本号,仅作标识,不影响实际文件名。
-
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→ 能提就提,不能提就普通权限。
-
操作系统兼容性声明(compatibility)
xml<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- 所有 supportedOS 节点都被注释掉了 --> </application> </compatibility>如果取消注释某个
supportedOSGUID,就等于告诉 Windows"我专门为这个版本做过测试,请别对我启用兼容层"。
示例 GUID:
{e2011457-1546-43c5-a5fe-008deee3d3f0}→ Windows Vista{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}→ Windows 10
全部注释掉时,Windows 会按"最老兼容模式"对待程序,可能启用旧版文件/注册表虚拟化。
-
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)。
-
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 文件夹下的)。
逐行速览
-
Microsoft.Bcl.AsyncInterfaces 1.1.0
给旧版 .NET Framework 提供
IAsyncEnumerable<T>、IAsyncDisposable等接口,使项目能在 4.7.2 上使用新版异步枚举器。 -
Npgsql 4.1.14
PostgreSQL 官方 .NET 驱动;4.1.14 是 2021 年前后的稳定版,支持 .NET Standard 2.0,因此能在 net472 运行。
-
System.Buffers 4.5.0
提供
ArrayPool<T>等缓冲池基础类型,减少 GC 压力;被 Npgsql、System.Text.Json 等内部依赖。 -
System.Memory 4.5.3
引入
Span<T>、Memory<T>、ReadOnlySequence<T>等高性能内存抽象;net472 本身没有这些类型,需要外置包。 -
System.Numerics.Vectors 4.5.0
提供硬件加速的 2D/3D/4D 向量类型(
Vector2,Vector3,Vector<T>),依赖 SIMD 指令集。 -
System.Runtime.CompilerServices.Unsafe 4.6.0
官方"不安全"小工具包,封装
Unsafe.Add、Unsafe.As等指针操作,被高性能库广泛引用。 -
System.Text.Encodings.Web 4.6.0
提供 HTML/JavaScript/URL 字符转义器,是 System.Text.Json 的底层依赖。
-
System.Text.Json 4.6.0
微软新一代高性能 JSON 序列化库;4.6.0 是 2019 年底发布,能在 net472 运行(基于 .NET Standard 2.0)。
-
System.Threading.Tasks.Extensions 4.5.3
补充
ValueTask<T>、IValueTaskSource等类型,让异步方法减少堆分配。 -
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老系统的开发者提供可直接套用的结构模板、配置样例与避坑指南,也为后续异步改造、云原生迁移奠定清晰边界。
(本课题全文完)