# P0-02 NX 版本探测 · 实测记录（2026-10-16）

> 执行环境：Windows Server 2022 · PowerShell 5.1
> 脚本路径：`D:\NX_AI\core\nx_versions.ps1`、`D:\NX_AI\core\nx_version_resolve.ps1`、`D:\NX_AI\tools\list_nx.ps1`
> JSON 输出：`D:\NX_AI\状态\versions.json`（UTF-8 无 BOM）

---

## 1. DryRun 输出（nx_versions.ps1 -DryRun）

```
===== nx_versions.ps1 -DryRun =====
将执行的操作：
  ① 扫描 HKLM:\SOFTWARE\Siemens 下 NX 子键
  ② 扫描 HKLM:\SOFTWARE\...\Uninstall（含 WOW6432Node）匹配 Siemens NX
  ③ 所有 FileSystem 盘符下 \Program Files\Siemens\NX* 兜底
  合并去重 → 校验 run_journal.exe / ugraf.exe → 计算能力位
  输出 JSON 到: D:\NX_AI\状态\versions.json
将探测的盘符：
  C:\  (系统)
  D:\
  E:\
  F:\  (系统)
===== DryRun 结束 =====
```

✓ 所有 FileSystem 盘符自动枚举（C/D/E/F），**无硬编码盘符**。

---

## 2. list_nx.ps1 实测输出

```
=== NX 版本探测结果 · 共 2 个 ===
--------------------------------------------------------------------
Version  Src   Root                                      RunJ  Graf MngD NXO  PyDll               PyVer CLR
---------------------------------------------------------------------------------------------------------
2212     Registry E:\Program Files\Siemens\NX2212            OK    OK   OK    OK   NXBIN\python310.dll 3.10  netfx4x
2412     Registry E:\Program Files\Siemens\NX2412            OK    OK   OK    OK   NXBIN\python311.dll 3.11  netfx4x
--------------------------------------------------------------------
```

### 2.1 能力位详情（NX2212）

```
  NX2212 (2212.1700.0.0)
    Root                    = E:\Program Files\Siemens\NX2212
    RunJournal FileVersion  = 2212.1700.0.0
    ugraf FileVersion       = 2212.1700.0.0
    NXOpen AssemblyVersion  = 2212.0.0.0
    NXOpen FileVersion      = 2212.0.0.0
    HasEmbeddedPython       = True
    PythonDll / Version     = NXBIN\python310.dll / 3.10
    SupportsCsharpJournal   = True
    ClrFlavor               = netfx4x
    Registry LICENSESERVER  = 29000@LocalHost
    Registry LANGUAGE       = simpl_chinese
```

### 2.2 能力位详情（NX2412）

```
  NX2412 (2412.5501.0.0)
    Root                    = E:\Program Files\Siemens\NX2412
    RunJournal FileVersion  = 2412.5501.0.0
    ugraf FileVersion       = 2412.7001.0.0
    NXOpen AssemblyVersion  = 2412.0.0.0
    NXOpen FileVersion      = 2412.0.0.0
    HasEmbeddedPython       = True
    PythonDll / Version     = NXBIN\python311.dll / 3.11
    SupportsCsharpJournal   = True
    ClrFlavor               = netfx4x
    Registry LICENSESERVER  = C:\Program Files\Siemens\PLMLicenseServer\splm13.lic;27800@localhost
    Registry LANGUAGE       = simpl_chinese
```

✓ **同时列出 NX2212 与 NX2412**（验收标准核心条目）。
✓ Root 正确指向 `E:\Program Files\Siemens\...`（无 C/D 盘硬编码）。
✓ 所有校验位 OK（run_journal / ugraf / managed / NXOpen.dll 全通过）。
✓ Python 版本正确识别：NX2212=3.10、NX2412=3.11（与 P0-01 实测一致）。
✓ CLR 风味 netfx4x（本机 .NET Framework 4.8，正确）。
✓ SupportsCsharpJournal=True（run_journal 内嵌 C# 编译器）。
✓ Source=Registry（权威注册表源优先命中，正确）。

---

## 3. nx_version_resolve.ps1 实测

### 3.1 指定版本 → 正确返回根

| 调用 | 返回 | exitcode |
|---|---|---|
| `-Version 2212` | `E:\Program Files\Siemens\NX2212` | 0 |
| `-Version 2412` | `E:\Program Files\Siemens\NX2412` | 0 |
| `-Version latest` | `E:\Program Files\Siemens\NX2412` | 0 |

✓ `latest` 自动取 VersionInt 最大的（2412 > 2212），正确。

### 3.2 不存在版本 → 清晰抛错（验收标准核心条目）

```
powershell -ExecutionPolicy Bypass -File nx_version_resolve.ps1 -Version 9999

[FAIL] 找不到 NX9999。本机已安装版本: 2212, 2412
exitcode = 1
```

✓ **不返回 $null**，而是明确的错误信息（含已安装版本列表）+ 非零退出码。

---

## 4. versions.json 内容（关键字段）

```json
{
  "GeneratedAt": "2026-10-16 13:23:15",
  "Count": 2,
  "NxVersions": [
    {
      "Version": "2212",
      "Root": "E:\\Program Files\\Siemens\\NX2212",
      "NormalizedRoot": "E:\\PROGRAM FILES\\SIEMENS\\NX2212",
      "DisplayVersion": "2212.1700.0.0",
      "Source": "Registry",
      "PythonVersion": "3.10",
      "ClrFlavor": "netfx4x",
      "SupportsCsharpJournal": true
    },
    {
      "Version": "2412",
      "Root": "E:\\Program Files\\Siemens\\NX2412",
      "NormalizedRoot": "E:\\PROGRAM FILES\\SIEMENS\\NX2412",
      "DisplayVersion": "2412.5501.0.0",
      "Source": "Registry",
      "PythonVersion": "3.11",
      "ClrFlavor": "netfx4x",
      "SupportsCsharpJournal": true
    }
  ]
}
```

---

## 5. 修复的 P0-02 问题回顾

| 问题 | 旧 install.ps1 行为 | 本方案行为 |
|---|---|---|
| 注册表键名错误 | 找 `HKLM\SOFTWARE\Siemens\Unigraphics Solutions\NX`（不存在） | 遍历 `HKLM\SOFTWARE\Siemens` 下所有 `NX <digits>` 子键（实测存在） |
| 硬编码盘符 | 默认路径表只覆盖 C:/D: | `Get-PSDrive -PSProvider FileSystem` 枚举所有盘符（实测覆盖 C/D/E/F） |
| 单源脆弱 | 只有 Uninstall 一个源 | **三源交叉**：Registry → Uninstall → Filesystem 兜底 |
| 找不到返回空 | 返回 $null 让上层猜 | throw 明确错误 + 含已安装版本列表 |
| 能力位缺失 | 只有路径，没有版本号/Python/CLR | PE FileVersion + AssemblyVersion + 内嵌 Python 版本 + CLR 风味全部计算 |

---

## 6. 关键设计决策

1. **源优先级 Registry > Uninstall > Filesystem**：注册表 INSTALLDIR 是权威路径（尾随 `\`），Uninstall 的 InstallLocation 有 DisplayVersion，Filesystem 只是兜底防止注册表被误删
2. **规范化键**：`(TrimEnd('\','/')).ToUpper()` 避免大小写/反斜杠导致重复（`E:\...\NX2412\` vs `e:\...\nx2412` 视为同一个）
3. **校验位**：只保留 run_journal.exe + ugraf.exe 都通过的版本（缺一不可，避免空壳目录被误识别）
4. **JSON UTF-8 无 BOM**（任务书硬性要求）——用 `System.Text.UTF8Encoding($false)` 写入
5. **PS5.1 中 Export-ModuleMember 完全放弃**——dot-source 引入后函数直接在当前作用域可用，Export-ModuleMember 仅对 .psm1 / Import-Module 有效
