借助网络配置,控制Modbus工具的日志显示,避免卡死界面
|
# 原生库嵌入与 zip 压缩说明
本文档说明 A1Lib 中原生 DLL 的嵌入方式,以及如何通过 zip 压缩减小程序集体积。
---
## 一、背景
A1Lib 需要随程序分发若干 ZLG / 创芯 / PEAK 的原生驱动 DLL:
| DLL | 用途 | 原始体积 |
|---|---|---|
| `ControlCAN.dll` | 创芯科技 CAN 卡驱动 | 2.56 MB |
| `PCANBasic.dll` | PEAK CAN 卡驱动 | 711 KB |
| `zlgcan.dll` | ZLG 致远 CAN 卡驱动(分发层) | 308 KB |
| `kerneldlls\*` | ZLG 各设备型号内核驱动 | 39 KB(精简后) |
这些文件是**未压缩的 PE 二进制**,而程序集嵌入资源本身不做压缩,
导致 `A1Lib.dll` 体积偏大。实测 ZIP 压缩率约 **45%**(约 2:1):
```
合计:3.59 MB -> 1.61 MB(压缩率 45%)
```
压缩率虽不及 7z(后者可达 3~8 倍),但 **.NET 内置 `System.IO.Compression`
即可解压,无需引入第三方库**,因此优先采用 ZIP。
---
## 二、工作机制
### 2.1 嵌入阶段(构建时)
`A1Lib.csproj` 采用「**zip 优先,裸文件兜底**」的双条件配置:
```xml
<ItemGroup Condition="Exists('CAN\ZLG_CAN\zlgcan.zip')">
<EmbeddedResource Include="CAN\ZLG_CAN\zlgcan.zip" LogicalName="ALib.ZLG_CAN.zlgcan.zip" />
</ItemGroup>
<ItemGroup Condition="!Exists('CAN\ZLG_CAN\zlgcan.zip')">
<EmbeddedResource Include="CAN\ZLG_CAN\zlgcan.dll" LogicalName="ALib.ZLG_CAN.zlgcan.dll" />
</ItemGroup>
```
- zip 存在 → 嵌入 zip,体积小
- zip 不存在 → 嵌入原始 DLL,**保证任何情况下都能构建成功**
因此 zip 是**可选优化**,删掉 zip 也能正常编译,只是程序集变大。
### 2.2 释放阶段(运行时)
首次使用某个原生库时自动解压还原到程序目录:
```
ZipResourceExtractor.ExtractFile() 单文件:{前缀}.{名去扩展名}.zip
ZipResourceExtractor.ExtractToDirectory() 目录 :{前缀}.{目录名}.zip
↑
DllResourceManager.ExtractDllFromResource() 按「zip 优先」选择资源
KernelDllExtractor.Extract() kerneldlls 目录
```
**资源名约定**(必须与 csproj 的 `LogicalName` 一致):
| 形态 | 约定 | 示例 |
|---|---|---|
| 单文件 | `{前缀}.{文件名去扩展名}.zip` | `ALib.ZLG_CAN.zlgcan.zip` |
| 目录 | `{前缀}.{目录名}.zip` | `ALib.ZLG_CAN.kerneldlls.zip` |
---
## 三、如何生成 zip
在 `A1Lib` 目录下执行打包脚本:
```powershell
.\pack_native_res.ps1
```
脚本会为每个目标生成同名 zip 并输出压缩统计:
```
目标 类型 文件数 原始 压缩后 压缩率
CAN\ZLG_CAN\zlgcan.dll file 1 307.5 KB 129.8 KB 42%
CAN\ZLG_CAN\kerneldlls dir 3 38.8 KB 17.9 KB 46%
CAN\CX_CAN\ControlCAN.dll file 1 2.56 MB 1.16 MB 45%
CAN\PCAN\PCANBasic.dll file 1 711.1 KB 310.2 KB 44%
合计:3.59 MB -> 1.61 MB(压缩率 45%)
```
仅打包单个目标:
```powershell
.\pack_native_res.ps1 -Target 'CAN\ZLG_CAN\kerneldlls'
```
> **注意**:脚本含中文输出,文件必须保存为 **UTF-8 with BOM**,
> 否则 Windows PowerShell 5.1 会按 ANSI 解析而报语法错误。
---
## 四、注意事项
### 4.1 原始文件保留,不参与编译
zip 与原始 DLL 同时存在于源码目录:
```
CAN/ZLG_CAN/
├── zlgcan.dll ← 保留:供打包、排查使用
├── zlgcan.zip ← 嵌入:实际随程序集发布
└── kerneldlls/ ← 保留:供打包、排查使用
```
csproj 通过 `Condition` 只嵌入其中之一,两者不会重复打包。
### 4.2 zip 内的目录结构
- **单文件 zip**:zip 内直接是该文件(`zlgcan.dll`)
- **目录 zip**:以**目录内容为根**,不含目录名本身
(即 `kerneldlls.zip` 内的条目是 `USBCAN.dll`、`dll_cfg.ini` 等,
而非 `kerneldlls/USBCAN.dll`),解压后直接铺到目标目录下。
这样 `kerneldlls\` 的相对路径才能被 `zlgcan.dll` 正确找到。
### 4.3 安全与健壮性
解压实现已考虑以下情况:
| 场景 | 处理 |
|---|---|
| zip 条目含 `..\` 路径穿越 | 校验目标路径必须落在解压目录内,否则跳过 |
| 文件已存在且大小一致 | 跳过写入,减少磁盘操作 |
| 目标文件被占用(驱动已加载) | 保留旧文件不报错,不影响使用 |
| 解压中断导致目录不完整 | `kerneldlls` 用 `dll_cfg.ini` 作完整性标记,缺失则重新解压 |
| 未打包 zip | 自动回退嵌入裸文件,功能不受影响 |
### 4.4 更换设备型号后需重新打包
若从 `ZLG_USBCAN/kerneldlls_original/` 取回了新的驱动文件,
**必须重新运行打包脚本**,否则 zip 内仍是旧内容。
---
## 五、后续可改进项
- **7z 支持**:7z 压缩率显著优于 ZIP(实测 kerneldlls 7.2MB → 932KB),
但需引入 `SevenZipSharp` / `SharpCompress` 等第三方库,
或改为调用外部 `7z.exe`。当前方案已用 ZIP 覆盖主要收益。
- **增量打包**:目前每次全量重打包,可按文件时间戳跳过未变更项。
- **构建时自动打包**:可加 MSBuild Target 在 `BeforeBuild` 自动调用脚本,
省去手动执行(代价是每次构建增加压缩耗时)。
---
## 六、相关文件
| 文件 | 职责 |
|---|---|
| `pack_native_res.ps1` | 打包脚本:生成各原生库的 zip |
| `ZipResourceExtractor.cs` | 通用 zip 资源释放(单文件 / 目录两种模式) |
| `DllResourceManager.cs` | 单个 DLL 的「zip 优先」释放与加载 |
| `CAN/ZLG_CAN/KernelDllExtractor.cs` | kerneldlls 目录的释放(zip 优先,逐个兜底) |
| `A1Lib.csproj` | 嵌入配置(zip 优先、裸文件兜底) |
|