From b665cb6b0497a8192c37c698b9cb1d21faa13f52 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 16:03:59 +0000 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BE=BF=E7=AD=BE?= =?UTF-8?q?=E9=87=8D=E5=90=AF=E5=90=8E=E4=B8=BB=E9=A2=98/=E5=AD=97?= =?UTF-8?q?=E4=BD=93=E4=B8=A2=E5=A4=B1=E5=92=8C=E5=B9=BD=E7=81=B5=E4=BE=BF?= =?UTF-8?q?=E7=AD=BE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根本原因:_DefaultSettings 注册表项被当作便签数据处理 - OPENED_NOTES 过滤掉以下划线开头的特殊键,避免 _DefaultSettings 被加载为便签 - DeleteAll() 保留特殊键,防止默认设置在重启时被误删 - LoadData 添加 _isLoading 标志,防止加载过程中 Title setter 用未初始化的 theme=0/style=0 提前写入注册表 - LoadData 直接设置 _theme/_style 字段并调用 ReloadTheme/ReloadStyle,避免 setter 副作用 - 安装器 MajorUpgrade Schedule 改为 afterInstallInitialize 确保覆盖升级顺序正确 - CustomAction 添加条件 NOT REMOVE~="ALL",卸载时不启动程序 https://claude.ai/code/session_01C8uHLbTTSPExtKZ7pNi56j --- Desktop Notes/Desktop Notes/MainForm.cs | 126 ++++++++++++++---------- Desktop Notes/Desktop Notes/REGISTRY.cs | 14 ++- Product.wxs | 4 +- 3 files changed, 88 insertions(+), 56 deletions(-) diff --git a/Desktop Notes/Desktop Notes/MainForm.cs b/Desktop Notes/Desktop Notes/MainForm.cs index 08c0ccc..3d191a8 100644 --- a/Desktop Notes/Desktop Notes/MainForm.cs +++ b/Desktop Notes/Desktop Notes/MainForm.cs @@ -39,73 +39,94 @@ public MainForm(int id, FormData dat = null) private void LoadData(FormData dat = null) { - //load defaults - this.StartPosition = FormStartPosition.WindowsDefaultLocation; - this.CreationTime = DateTime.Now; - if (string.IsNullOrEmpty(Title)) - { - Title = string.Format("Note {0:##}", FORM_ID); - } - - // 如果是新建便签,应用上次保存的默认设置 - if (dat == null) + _isLoading = true; + try { - DefaultNoteSettings defaultSettings = REGISTRY.GetDefaultSettings(); - this.Opacity = defaultSettings.opacity; - - if (defaultSettings.customTheme != null) + //load defaults + this.StartPosition = FormStartPosition.WindowsDefaultLocation; + this.CreationTime = DateTime.Now; + if (string.IsNullOrEmpty(titlebar.Text)) { - this.CustomTheme = defaultSettings.customTheme; - } - if (defaultSettings.customStyle != null) - { - this.CustomStyle = defaultSettings.customStyle; + titlebar.Text = string.Format("Note {0:##}", FORM_ID); } - if (Program.Themes.Count > defaultSettings.theme) + // 如果是新建便签,应用上次保存的默认设置 + if (dat == null) { - CurrentTheme = defaultSettings.theme; - } - else if (Program.Themes.Count > 1) - { - CurrentTheme = 1; + DefaultNoteSettings defaultSettings = REGISTRY.GetDefaultSettings(); + this.Opacity = defaultSettings.opacity; + + if (defaultSettings.customTheme != null) + { + this.CustomTheme = defaultSettings.customTheme; + } + if (defaultSettings.customStyle != null) + { + this.CustomStyle = defaultSettings.customStyle; + } + + if (Program.Themes.Count > defaultSettings.theme) + { + _theme = defaultSettings.theme; + } + else if (Program.Themes.Count > 1) + { + _theme = 1; + } + ReloadTheme(); + + if (Program.Styles.Count > defaultSettings.style) + { + _style = defaultSettings.style; + } + else if (Program.Styles.Count > 1) + { + _style = 1; + } + ReloadStyle(); + + _isLoading = false; + Save(); + this.Show(); + return; } - if (Program.Styles.Count > defaultSettings.style) - { - CurrentStyle = defaultSettings.style; - } - else if (Program.Styles.Count > 1) - { - CurrentStyle = 1; - } + //load others + this.SuspendLayout(); + this.StartPosition = FormStartPosition.Manual; + this.Location = dat.Location; + this.Size = dat.FormSize; + this.notebox1.Rtf = dat.data; + this.Opacity = dat.opacity; + titlebar.Text = dat.title; + if (dat.customTheme != null) { this.CustomTheme = dat.customTheme; } + _theme = dat.theme; + if (_theme >= Program.Themes.Count) _theme = Program.Themes.Count - 1; + if (_theme < 0) _theme = 0; + ReloadTheme(); + if (dat.customStyle != null) { this.CustomStyle = dat.customStyle; } + _style = dat.currentStyle; + if (_style >= Program.Styles.Count) _style = Program.Styles.Count - 1; + if (_style < 0) _style = 0; + ReloadStyle(); + if (dat.creationTime > new DateTime(2014, 1, 1)) { this.CreationTime = dat.creationTime; } + this.ResumeLayout(true); + _isLoading = false; + Save(); this.Show(); - return; + if (dat.hidden) this.Hide(); + } + finally + { + _isLoading = false; } - - //load others - this.SuspendLayout(); - this.StartPosition = FormStartPosition.Manual; - this.Location = dat.Location; - this.Size = dat.FormSize; - this.notebox1.Rtf = dat.data; - this.Opacity = dat.opacity; - this.Title = dat.title; - if (dat.customTheme != null) { this.CustomTheme = dat.customTheme; } - this.CurrentTheme = dat.theme; - if (dat.customStyle != null) { this.CustomStyle = dat.customStyle; } - this.CurrentStyle = dat.currentStyle; - if (dat.creationTime > new DateTime(2014, 1, 1)) { this.CreationTime = dat.creationTime; } - this.ResumeLayout(true); - - this.Show(); - if (dat.hidden) this.Hide(); } // // Properties and Variables // + private bool _isLoading = false; public DateTime CreationTime { get; set; } public int FORM_ID { get; set; } public string Title @@ -217,6 +238,7 @@ private void TopBar_MouseDown(object sender, System.Windows.Forms.MouseEventArgs // public bool Save() { + if (_isLoading) return true; try { REGISTRY.SetData(FORM_ID.ToString(), new FormData(this)); diff --git a/Desktop Notes/Desktop Notes/REGISTRY.cs b/Desktop Notes/Desktop Notes/REGISTRY.cs index 24ad7e0..32f2814 100644 --- a/Desktop Notes/Desktop Notes/REGISTRY.cs +++ b/Desktop Notes/Desktop Notes/REGISTRY.cs @@ -1,5 +1,6 @@ using Microsoft.Win32; using Newtonsoft.Json; +using System.Collections.Generic; using System.Windows.Forms; namespace Desktop_Notes @@ -14,7 +15,14 @@ public static string[] OPENED_NOTES { get { - return REG_PATH.GetValueNames(); + List notes = new List(); + foreach (string name in REG_PATH.GetValueNames()) + { + // 过滤掉非便签数据的注册表项 + if (name.StartsWith("_")) continue; + notes.Add(name); + } + return notes.ToArray(); } } @@ -39,8 +47,10 @@ public static void Delete(string id) public static void DeleteAll() { - foreach (string val in OPENED_NOTES) + foreach (string val in REG_PATH.GetValueNames()) { + // 保留以下划线开头的特殊键(如 _DefaultSettings) + if (val.StartsWith("_")) continue; REG_PATH.DeleteValue(val); } } diff --git a/Product.wxs b/Product.wxs index 3dfcee9..3148447 100644 --- a/Product.wxs +++ b/Product.wxs @@ -24,11 +24,11 @@ - + From 89a914942fa0b899f93c281dec77215665d58eab Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 16:11:29 +0000 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E5=B0=86=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E4=BB=8E=E6=B3=A8=E5=86=8C=E8=A1=A8=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=88=B0=E6=96=87=E4=BB=B6=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 便签数据存储到 %AppData%\Desktop_Notes\notes\{id}.json - 默认设置存储到 %AppData%\Desktop_Notes\_DefaultSettings.json - 启动时自动检测注册表中的存量数据并迁移到文件目录 - 迁移完成后清理注册表中的旧数据和空键 - StartWithWindows 和 FirstRun 仍使用注册表(属于系统级设置) - 保持 REGISTRY 类的公共 API 不变,所有调用方无需修改 https://claude.ai/code/session_01C8uHLbTTSPExtKZ7pNi56j --- Desktop Notes/Desktop Notes/REGISTRY.cs | 227 +++++++++++++++++++----- 1 file changed, 183 insertions(+), 44 deletions(-) diff --git a/Desktop Notes/Desktop Notes/REGISTRY.cs b/Desktop Notes/Desktop Notes/REGISTRY.cs index 32f2814..aac0b35 100644 --- a/Desktop Notes/Desktop Notes/REGISTRY.cs +++ b/Desktop Notes/Desktop Notes/REGISTRY.cs @@ -1,25 +1,140 @@ -using Microsoft.Win32; +using Microsoft.Win32; using Newtonsoft.Json; +using System; using System.Collections.Generic; +using System.IO; using System.Windows.Forms; namespace Desktop_Notes { public static class REGISTRY { + // 数据存储目录:%AppData%\Desktop_Notes\ + private static readonly string DATA_DIR = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Desktop_Notes"); - public static RegistryKey REG_PATH = Registry.CurrentUser.CreateSubKey("Software") - .CreateSubKey("Sand Soft").CreateSubKey("Desktop Notes"); + private static readonly string NOTES_DIR = Path.Combine(DATA_DIR, "notes"); + private const string DEFAULT_SETTINGS_FILE = "_DefaultSettings.json"; + + // 旧版注册表路径(用于迁移存量用户数据) + private const string OLD_REG_PATH = @"Software\Sand Soft\Desktop Notes"; + + static REGISTRY() + { + // 确保数据目录存在 + if (!Directory.Exists(DATA_DIR)) + Directory.CreateDirectory(DATA_DIR); + if (!Directory.Exists(NOTES_DIR)) + Directory.CreateDirectory(NOTES_DIR); + + // 检测并迁移存量用户的注册表数据 + MigrateFromRegistry(); + } + + #region 注册表数据迁移 + + /// + /// 检测注册表中是否有旧版数据,如果有则迁移到文件存储 + /// + private static void MigrateFromRegistry() + { + try + { + RegistryKey regKey = Registry.CurrentUser.OpenSubKey(OLD_REG_PATH, true); + if (regKey == null) return; + + string[] valueNames = regKey.GetValueNames(); + if (valueNames.Length == 0) + { + regKey.Close(); + return; + } + + bool hasMigratedData = false; + + foreach (string name in valueNames) + { + try + { + object val = regKey.GetValue(name, null); + if (val == null) continue; + string json = (string)val; + + if (name == "_DefaultSettings") + { + // 迁移默认设置 + string settingsPath = Path.Combine(DATA_DIR, DEFAULT_SETTINGS_FILE); + if (!File.Exists(settingsPath)) + { + File.WriteAllText(settingsPath, json); + hasMigratedData = true; + } + } + else if (!name.StartsWith("_")) + { + // 迁移便签数据 + string notePath = Path.Combine(NOTES_DIR, name + ".json"); + if (!File.Exists(notePath)) + { + File.WriteAllText(notePath, json); + hasMigratedData = true; + } + } + } + catch { } + } + + // 迁移成功后清理注册表中的旧数据 + if (hasMigratedData) + { + foreach (string name in valueNames) + { + try { regKey.DeleteValue(name, false); } + catch { } + } + } + + regKey.Close(); + + // 尝试清理空的注册表键 + try + { + RegistryKey parentKey = Registry.CurrentUser.OpenSubKey(@"Software\Sand Soft", true); + if (parentKey != null) + { + RegistryKey check = parentKey.OpenSubKey("Desktop Notes"); + if (check != null && check.GetValueNames().Length == 0 && check.SubKeyCount == 0) + { + check.Close(); + parentKey.DeleteSubKey("Desktop Notes", false); + } + else if (check != null) + { + check.Close(); + } + parentKey.Close(); + } + } + catch { } + } + catch { } + } + + #endregion + + #region 便签数据存储(文件) public static string[] OPENED_NOTES { get { List notes = new List(); - foreach (string name in REG_PATH.GetValueNames()) + if (!Directory.Exists(NOTES_DIR)) return notes.ToArray(); + + foreach (string file in Directory.GetFiles(NOTES_DIR, "*.json")) { - // 过滤掉非便签数据的注册表项 - if (name.StartsWith("_")) continue; + string name = Path.GetFileNameWithoutExtension(file); notes.Add(name); } return notes.ToArray(); @@ -28,33 +143,84 @@ public static string[] OPENED_NOTES public static void SetData(string id, FormData data) { - string dat = JsonConvert.SerializeObject(data); - REG_PATH.SetValue(id, dat); + try + { + string json = JsonConvert.SerializeObject(data, Formatting.Indented); + string path = Path.Combine(NOTES_DIR, id + ".json"); + File.WriteAllText(path, json); + } + catch { } } public static FormData GetData(string id) { - object dat = REG_PATH.GetValue(id, null); - if (dat == null) return null; - return JsonConvert.DeserializeObject((string)dat); + try + { + string path = Path.Combine(NOTES_DIR, id + ".json"); + if (!File.Exists(path)) return null; + string json = File.ReadAllText(path); + return JsonConvert.DeserializeObject(json); + } + catch { return null; } } public static void Delete(string id) { - try { REG_PATH.DeleteValue(id); } + try + { + string path = Path.Combine(NOTES_DIR, id + ".json"); + if (File.Exists(path)) File.Delete(path); + } catch { } } public static void DeleteAll() { - foreach (string val in REG_PATH.GetValueNames()) + try + { + if (!Directory.Exists(NOTES_DIR)) return; + foreach (string file in Directory.GetFiles(NOTES_DIR, "*.json")) + { + File.Delete(file); + } + } + catch { } + } + + #endregion + + #region 默认便签设置(文件) + + public static void SaveDefaultSettings(DefaultNoteSettings settings) + { + try + { + string json = JsonConvert.SerializeObject(settings, Formatting.Indented); + string path = Path.Combine(DATA_DIR, DEFAULT_SETTINGS_FILE); + File.WriteAllText(path, json); + } + catch { } + } + + public static DefaultNoteSettings GetDefaultSettings() + { + try + { + string path = Path.Combine(DATA_DIR, DEFAULT_SETTINGS_FILE); + if (!File.Exists(path)) return new DefaultNoteSettings(); + string json = File.ReadAllText(path); + return JsonConvert.DeserializeObject(json); + } + catch { - // 保留以下划线开头的特殊键(如 _DefaultSettings) - if (val.StartsWith("_")) continue; - REG_PATH.DeleteValue(val); + return new DefaultNoteSettings(); } } + #endregion + + #region 系统设置(仍使用注册表) + public static RegistryKey START_KEY = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); public static bool StartWithWindows { @@ -86,33 +252,6 @@ public static bool FirstRun } } - // 默认便签设置的注册表键名 - private const string DEFAULT_SETTINGS_KEY = "_DefaultSettings"; - - // 保存默认便签设置 - public static void SaveDefaultSettings(DefaultNoteSettings settings) - { - try - { - string json = JsonConvert.SerializeObject(settings); - REG_PATH.SetValue(DEFAULT_SETTINGS_KEY, json); - } - catch { } - } - - // 读取默认便签设置 - public static DefaultNoteSettings GetDefaultSettings() - { - try - { - object dat = REG_PATH.GetValue(DEFAULT_SETTINGS_KEY, null); - if (dat == null) return new DefaultNoteSettings(); - return JsonConvert.DeserializeObject((string)dat); - } - catch - { - return new DefaultNoteSettings(); - } - } + #endregion } } From 5c4b99daefb40b6bf41117cb9142a6e8b9aa8ad8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 16:16:15 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20WiX=20v4=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Custom 元素的条件从 inner text 改为 Condition 属性(WIX0400) - DesktopFolder 从 DirectoryRef 改为 StandardDirectory(WIX5436) - 移除顶部重复的 StandardDirectory DesktopFolder 声明 - RemoveFolder Id 改名避免与 StandardDirectory Id 冲突 https://claude.ai/code/session_01C8uHLbTTSPExtKZ7pNi56j --- Product.wxs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Product.wxs b/Product.wxs index 3148447..b569bb9 100644 --- a/Product.wxs +++ b/Product.wxs @@ -21,14 +21,13 @@ - - + @@ -60,12 +59,12 @@ - + - + - +