{"record":{"id":"a2b380b9b78c7b18","repo":"OdysseusYuan/LKY_OfficeTools","slug":"perpetualvl2021","errorCode":null,"errorMessage":"无法获取 PerpetualVL2021 对应的版本号！","messagePattern":"无法获取 PerpetualVL2021 对应的版本号！","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"LKY_OfficeTools/Lib/Lib_OfficeInfo.cs","lineNumber":136,"sourceCode":"                        {\n                            return null;\n                        }\n\n                        new Log(\"\\n------> 正在解析 最新可用 Office 版本 ...\", ConsoleColor.DarkCyan);\n\n                        //获取版本信息\n                        string latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, \"\\\"PerpetualVL2021\\\",\", \"name\");                     //获取 2021 LTSC\n                        if (!string.IsNullOrEmpty(latest_info))\n                        {\n                            //获取版本号\n                            var ver = new Version(Com_TextOS.GetCenterText(latest_info, \"latestUpdateVersion\\\":\\\"\", \"\\\"\"));      //官方Json取值，格式和自己的不一样，千万注意\n\n                            new Log($\"     √ 已获得 Office 最新版本为：v{ver}\", ConsoleColor.DarkGreen);\n\n                            return _office_latest_version = ver;\n                        }\n\n                        throw new Exception(\"无法获取 PerpetualVL2021 对应的版本号！\");\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(\"     × 无法获取 最新可用 Office 版本，请稍后重试！\", ConsoleColor.DarkRed);\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n\n            private static string _office_url_root;\n            internal static string OfficeUrlRoot\n            {\n                get\n                {\n                    try\n                    {\n                        //非空返回","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/OdysseusYuan/LKY_OfficeTools/blob/6f9a1bd471918f3310e6be4a6d7f42d0d6e467cf/LKY_OfficeTools/Lib/Lib_OfficeInfo.cs#L118-L154","documentation":"Thrown when the PerpetualVL2021 (Office 2021 LTSC) channel block's version cannot be parsed from OfficeChannelInfo. The code uses Com_TextOS.GetCenterText with string markers (\"PerpetualVL2021\", ... name) to extract the block, then reads latestUpdateVersion. This fires when the PerpetualVL2021 block is absent from Microsoft's channel data (channel renamed/removed) or when the version field marker is not found within it. It is fragile string-scraping of Microsoft's channel XML/JSON rather than structured parsing.","triggerScenarios":"(a) Microsoft's channel data no longer contains a \"PerpetualVL2021\" channel block (renamed or deprecated); (b) the block exists but the latestUpdateVersion field marker is absent or reformatted; (c) the extracted version string is malformed so new Version() throws (that case is caught by the outer handler and returns null, but indicates the same root cause).","commonSituations":"Microsoft restructured or renamed the Office 2021 LTSC channel; the channel JSON field ordering/format changed; only Microsoft 365 channels are published and PerpetualVL2021 was dropped; Microsoft changed escaping in the JSON so the raw-quote markers no longer match.","solutions":["Dump the raw OfficeChannelInfo content to inspect whether a \"PerpetualVL2021\" block still exists.","If the block was renamed, update the left marker in GetCenterText to the new channel identifier.","If latestUpdateVersion was renamed, update the inner marker string to match Microsoft's current field name.","Replace string-marker scraping with a real JSON parser (e.g. System.Text.Json / Newtonsoft) to survive field reordering.","Add a unit test against a saved sample of the channel JSON so format drift is caught early."],"exampleFix":"// before\nstring latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, \"\\\"PerpetualVL2021\\\",\", \"name\");\nif (!string.IsNullOrEmpty(latest_info))\n{\n    var ver = new Version(Com_TextOS.GetCenterText(latest_info, \"latestUpdateVersion\\\":\\\"\", \"\\\"\"));\n    return _office_latest_version = ver;\n}\nthrow new Exception(\"无法获取 PerpetualVL2021 对应的版本号！\");\n\n// after — structured parsing with a clear missing-channel error\nvar json = JObject.Parse(OfficeChannelInfo);\nvar channel = json[\"PerpetualVL2021\"];\nif (channel == null)\n    throw new Exception(\"PerpetualVL2021 频道不存在，可能已被微软重命名或移除。\");\nvar ver = new Version(channel[\"latestUpdateVersion\"].ToString());\nreturn _office_latest_version = ver;","handlingStrategy":"fallback","validationCode":"// Sanity-check the channel block presence before parsing the version.\nstring latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, \"\\\"PerpetualVL2021\\\",\", \"name\");\nif (string.IsNullOrEmpty(latest_info))\n{\n    new Log(\"OfficeChannelInfo 中未找到 PerpetualVL2021 频道块，微软可能已更改频道结构。\", ConsoleColor.DarkRed);\n    return;\n}","typeGuard":"// Narrow a parsed version string before constructing Version (which throws on malformed input).\nbool TryParseOfficeVersion(string raw, out Version ver)\n{\n    ver = null;\n    if (string.IsNullOrWhiteSpace(raw)) return false;\n    return Version.TryParse(raw.Trim('\"', ' '), out ver);\n}","tryCatchPattern":null,"preventionTips":["Prefer a real JSON parser over GetCenterText string markers; field reordering then cannot break parsing.","Monitor Microsoft's Office channel documentation for PerpetualVL2021 naming changes.","Keep a captured sample of the channel JSON in a unit test to catch format drift early.","Wrap new Version(...) in TryParse to avoid an exception masking the real missing-field cause."],"tags":["office","json-parsing","version","fragile-parsing","ltsc"],"backgroundTag":null,"analyzedSha":"6f9a1bd471918f3310e6be4a6d7f42d0d6e467cf","analyzedAt":"2026-08-13T18:00:34.810Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}