# PrintSetting 崩溃风险分析报告 ## 涉及文件 | 文件 | 说明 | |------|------| | [Printsetting.h](../Printsetting.h) | `PrintSettingModel` 结构体 + `PrintSetting` 类声明 | | [Printsetting.cpp](../Printsetting.cpp) | `PrintSetting` 类实现 + `PrintSettingModel` 静态方法实现 | | [testprintwidget.cpp](../testprintwidget.cpp) | `TestPrintWidget` 中调用 `m_printSetting` 的代码 | | [InkJetPrinter.cpp](../InkJetPrinter.cpp) | `InkJetPrinter` 中使用 `m_PrintSetting` 的代码 | --- ## 🔴 高风险(必定崩溃) ### 1. `applyPrintSettingsToSysPara()` — `sysPara->devOp` 在 if 保护块外被访问 **文件**: [Printsetting.cpp:600-609](../Printsetting.cpp#L600-L609) ```cpp void PrintSetting::applyPrintSettingsToSysPara(bool showTip) { updateModelFromUI(); PrintSettingModel::applyToSysPara(m_model, sysPara); // L600: 无 null 检查 if (sysPara) { // L602: 有检查 sysPara->devOp->setHighVoltageAmplitudetoSTM32(); sysPara->devOp->writeFpgaCalandar(0); sysPara->devOp->writeFlightCompCtrl(...); } if(sysPara->devOp->writeFpgaPrintPara(true) != -1 && showTip) { // L609: ❌ 在 if(sysPara) 之外! QMessageBox box(...); box.exec(); } if(sysPara) sysPara->saveParaToFile(); } ``` **问题**: - L600 调用 `PrintSettingModel::applyToSysPara(m_model, sysPara)` 时未检查 `sysPara` 是否为 null。 - L609 的 `sysPara->devOp->writeFpgaPrintPara(true)` 在 `if(sysPara)` 保护块**之外**,如果 `sysPara` 为 null,直接崩溃。 - L602-L606 虽在 `if(sysPara)` 内,但 `sysPara->devOp` 本身也可能为 null。 **触发场景**:`bindSysPara()` 未被调用或 `sysPara` 被意外置空时,用户点击"应用"按钮。 --- ### 2. `PrintSettingModel::applyToSysPara()` — `sysPara` 参数无 null 检查 **文件**: [Printsetting.cpp:743-799](../Printsetting.cpp#L743-L799) ```cpp void PrintSettingModel::applyToSysPara(const PrintSettingModel &model, SysPara *sysPara) { sysPara->para.leftRightInverse = model.leftRightInverse; // ❌ 无 null 检查 sysPara->para.upDownInverse = model.upDownInverse; // ... 后续约 30 行全部直接访问 sysPara->para.xxx } ``` **问题**:整个函数体没有对 `sysPara` 做 null 检查,直接解引用。如果传入 null,立即崩溃。 **调用链**: - `PrintSetting::applyPrintSettingsToSysPara()` → `PrintSettingModel::applyToSysPara(m_model, sysPara)` - `PrintSetting::bindSysPara()` → `PrintSettingModel::fromSysPara(sysPara)` (同样无 null 检查,见下文) --- ### 3. `PrintSettingModel::fromSysPara()` — `sysPara` 参数无 null 检查 **文件**: [Printsetting.cpp:802-845](../Printsetting.cpp#L802-L845) ```cpp PrintSettingModel PrintSettingModel::fromSysPara(SysPara *sysPara) { PrintSettingModel model; model.leftRightInverse = sysPara->para.leftRightInverse; // ❌ 无 null 检查 model.upDownInverse = sysPara->para.upDownInverse; // ... 后续约 30 行全部直接访问 sysPara->para.xxx } ``` **问题**:与 `applyToSysPara()` 相同,整个函数体没有对 `sysPara` 做 null 检查。 **调用链**: - `PrintSetting::bindSysPara()` → `PrintSettingModel::fromSysPara(sysPara)` --- ### 4. `on_pushButtonApplyPulseFix_clicked()` — `sysPara` 可能为 null **文件**: [Printsetting.cpp:287-290](../Printsetting.cpp#L287-L290) ```cpp void PrintSetting::on_pushButtonApplyPulseFix_clicked() { pulseNumFix->bindSysPara(sysPara); // ❌ sysPara 可能为 null pulseNumFix->hide(); pulseNumFix->show(); } ``` **问题**:未检查 `sysPara` 是否为 null,直接传给 `pulseNumFix->bindSysPara()`。 --- ### 5. TestPrintWidget 中 `m_printSetting` 空指针解引用(3处) 已在 [TestPrintWidget 崩溃风险分析报告](./TestPrintWidget_Crash_Risk_Analysis.md) 中详述,此处汇总: | 位置 | 代码 | 问题 | |------|------|------| | [testprintwidget.cpp:435](../testprintwidget.cpp#L435) | `PrintSettingModel::saveToXml(m_printSetting->model(), writer)` | `m_printSetting` 可能为 null | | [testprintwidget.cpp:621-622](../testprintwidget.cpp#L621-L622) | `m_printSetting->setModel(model)` / `m_printSetting->applyPrintSettingsToSysPara(false)` | `m_printSetting` 可能为 null | | [testprintwidget.cpp:1122](../testprintwidget.cpp#L1122) | `m_printSetting->setModel(defModel)` | `m_printSetting` 可能为 null | --- ## 🟡 中风险(特定条件下崩溃) ### 6. `applyPrintSettingsToSysPara()` — `sysPara->devOp` 可能为 null **文件**: [Printsetting.cpp:603-606](../Printsetting.cpp#L603-L606) ```cpp if (sysPara) { sysPara->devOp->setHighVoltageAmplitudetoSTM32(); // devOp 可能为 null sysPara->devOp->writeFpgaCalandar(0); // devOp 可能为 null sysPara->devOp->writeFlightCompCtrl(...); // devOp 可能为 null } ``` **问题**:虽然检查了 `sysPara`,但 `sysPara->devOp` 指针本身可能为 null(取决于 `SysPara` 的初始化时机)。 --- ### 7. `on_checkEyeTriggerSynPrintEn_clicked()` — `sysPara` 有检查但 `comboBoxSyncActType` 可能越界 **文件**: [Printsetting.cpp:302-340](../Printsetting.cpp#L302-L340) ```cpp void PrintSetting::on_checkEyeTriggerSynPrintEn_clicked(bool checked) { if(sysPara) // ✅ 有 null 检查 { // ... if(ui->comboBoxSyncActType->currentIndex() >= ui->comboBoxSyncActType->count() || ui->comboBoxSyncActType->currentIndex() == -1) { ui->comboBoxSyncActType->setCurrentIndex(0); } } } ``` **问题**:`comboBoxSyncActType` 的 `currentIndex()` 值为 -1 时的处理是正确的,但如果 comboBox 未正确初始化(count() 为 0),`setCurrentIndex(0)` 可能无效,但不会崩溃。此处理论上安全,标注为中风险仅供参考。 --- ## 🟢 低风险(逻辑瑕疵,不太可能崩溃) ### 8. `PrintSettingModel::toString()` — 格式说明符类型不匹配 **文件**: [Printsetting.cpp:849-856](../Printsetting.cpp#L849-L856) ```cpp QString PrintSettingModel::toString(const PrintSettingModel &model) { QString str = QString::asprintf("LRInverse: %d, ... triggerInterval: %d, ... eleEyeRepeatCnt: %d, ...", model.triggerInterval, ... model.eleEyeRepeatCnt, ...); } ``` **问题**:`model.triggerInterval` 和 `model.eleEyeRepeatCnt` 是 `unsigned` 类型,但格式说明符用了 `%d`(有符号)。不会崩溃,但可能输出异常值(如大数字显示为负数)。 --- ### 9. `PrintSettingModel::defPrintSettingModel()` — 文件不存在时静默返回默认值 **文件**: [Printsetting.cpp:860-873](../Printsetting.cpp#L860-L873) ```cpp PrintSettingModel PrintSettingModel::defPrintSettingModel() { QFile file(DEF_PRINT_SETTINGS_PATH); if (!file.open(QIODevice::ReadOnly)) { return PrintSettingModel(); // 静默返回默认构造 } // ... } ``` **问题**:文件打开失败时没有日志输出,不利于调试。不会崩溃。 --- ### 10. `updateModelFromUI()` — 无返回值校验 **文件**: [Printsetting.cpp:114-170](../Printsetting.cpp#L114-L170) ```cpp m_model.encoderPulsePerMm = ui->lineEditTriggerPulsePerMm->text().toFloat(); ``` **问题**:`toFloat()` 在输入为空时返回 0.0,不会崩溃。但如果用户输入了非法字符(虽然有 `QDoubleValidator`),结果可能不符合预期。 --- ### 11. `on_radioButtonEleEyeLevelTrigger_pressed()` 等函数中 `setEnableSynTriggerSetting` 的判断逻辑 **文件**: [Printsetting.cpp:310-325](../Printsetting.cpp#L310-L325) ```cpp void PrintSetting::on_radioButtonEleEyeEdgetTrigger_pressed() { setTriggerUI(PRINT_TRIGGER_ELEC_EYE_EDGE); if(ui->radioButtonSynTriggerExternal->isChecked()) setEnableSynTriggerSetting(true); else setEnableSynTriggerSetting(false); } ``` **问题**:这些函数未检查 `sysPara` 是否为 null 就调用 `setTriggerUI`,但 `setTriggerUI` 只操作 `m_model` 和 UI 控件,不依赖 `sysPara`,所以安全。 --- ## 📋 修复建议汇总 | 优先级 | 位置 | 问题 | 修复方案 | |--------|------|------|----------| | **P0** | `applyPrintSettingsToSysPara()` L600 | `sysPara` 未检查就传给 `applyToSysPara` | 在 L600 前加 `if(!sysPara) return;` | | **P0** | `applyPrintSettingsToSysPara()` L609 | `sysPara->devOp` 在 if 块外访问 | 将 L609-L614 移入 `if(sysPara)` 块内 | | **P0** | `PrintSettingModel::applyToSysPara()` L743 | `sysPara` 无 null 检查 | 函数开头加 `if(!sysPara) return;` | | **P0** | `PrintSettingModel::fromSysPara()` L802 | `sysPara` 无 null 检查 | 函数开头加 `if(!sysPara) return PrintSettingModel();` | | **P0** | `on_pushButtonApplyPulseFix_clicked()` L287 | `sysPara` 未检查 | 加 `if(!sysPara) return;` | | **P1** | `applyPrintSettingsToSysPara()` L603 | `sysPara->devOp` 可能 null | 加 `if(sysPara->devOp)` 判断 | | **P1** | `testprintwidget.cpp` 多处 | `m_printSetting` 可能 null | 加 `if(m_printSetting)` 判断(共3处) | --- ## 崩溃调用链总结 ``` 用户点击"应用"按钮 → PrintSetting::on_pushButtonApply_pressed() → PrintSetting::applyPrintSettingsToSysPara(true) → PrintSettingModel::applyToSysPara(m_model, sysPara) ← ❌ sysPara 可能 null → sysPara->devOp->writeFpgaPrintPara(true) ← ❌ 在 if(sysPara) 外 ``` ``` 用户点击"脉冲修正"按钮 → PrintSetting::on_pushButtonApplyPulseFix_clicked() → pulseNumFix->bindSysPara(sysPara) ← ❌ sysPara 可能 null ``` ``` 加载/新建文件 → TestPrintWidget::saveFile() / parseVbfile() / on_newfileButton_pressed() → m_printSetting->model() / setModel() / applyPrintSettingsToSysPara() ← ❌ m_printSetting 可能 null ``` --- ## 生成时间 2026-06-29