2026年3月

TSFTool 是 IMETool 的继承者,是运行在 Windows10-11/Windows Server 2019-2025 操作系统下的输入法设置工具,支持 TSF 输入法框架,并部分兼容旧的 IMM 输入法框架。

下载:TSFTool.rar

tsftool.png

版本历史:
0.9.3.3: 修正界面按键位置错位问题。Windows 现在已经修正了 ~ 热键无法生效的问题,故热键列表也做了调整。
0.9.3.2:优化异常处理,增加了日志功能(/log 参数启用日志,或者从其它功能菜单下打开),便于定位错误。
去除了一段测试后忘记删除的代码。无其它功能更新。
0.9.3.1:修订命令行参数 /remove_eng 失效问题,去除无用的 /? 参数。同时发现程序在最新版的 Windows11 下有时退出后仍在进程中,尚未找到原因。
0.9.3.0:解决命令行输入两个以上参数时,程序界面不自动退出的问题。将启用/禁用输入法的延时改为5秒。修正默认输入法设置为“美式键盘”时程序不能正确识别的问题。优化“每个窗口使用不同输入法”设置的生效速度。
0.9.2.9:增加命令行参数 /remove_eng,对应其它菜单下的删除顽固的英语美式键盘功能(不删除注册表项目)。
0.9.2.8:修订因输入法名称中含有空格和大写字母带来的命令行参数 bug,受影响的命令:
add_name/del_name/move_name/default_name/hotkey_name。
注意:使用 /move_name 和 /hotkey_name 命令时,如输入法名称中含有空格,需要使用双引号括起来。
0.9.2.7:修改导出/导出配置文件格式,解决某些特殊情况下可能出现冲突的问题。
0.9.2.6:增加导出/导出配置功能(在其它功能菜单下)。去除 0.9.2.5 增加的禁用所有输入法功能。
0.9.2.5:增加禁用所有输入法功能(因为必须保留至少一个输入法,故全部禁用后保留了一个美式键盘)。
0.9.2.4:增加导出输入法列表功能。取消等待4秒的对话框。
0.9.2.3:修正两处 bug。
0.9.2.2:旧的 IMM 框架输入法在列表中显示为绿色。增加删除按钮,从注册表中删除无用输入法项目交由用户处理。
0.9.2.1:在启用/禁用输入法后强制延时4秒,避免系统操作太慢可能会导致程序报错。此版本可以清除小狼毫等输入法卸载后在注册表中的残留项目。
0.9.2.0:增加命令行参数支持;增加帮助窗口;一些细微的程序内部调整。
0.9.1.3:修补从 0.9.1.0 版本开始遗漏的写入键盘切换热键的部分代码(感谢 waiting 的提醒)。
0.9.1.2:旧版本的 Windows10 不支持表情符号面板功能,TSFTool 启动时会报错。此版本修订这一问题。
0.9.1.1:某种输入法禁用时,如果其已被设置为默认输入法,则将默认输入法改为语言列表。英文键盘布局在禁用/启用时,增加一个延时关闭的提示对话框,以免操作过快引起异常。增加删除顽固存在的英语美式键盘布局功能。
0.9.1.0:全部重写代码,使用不同的方式处理,支持旧的 IMM 框架输入法,修复一些 Bug。此版本需要管理员权限。增加 32 位版本,32位版本也可以正常支持 64 位的 Windows10。
0.9.0.0:初始版本,只支持 TSF框架输入法。

命令行参数:
/log /log:[日志路径] /log [日志路径] 启用日志,路径可选。默认是在主程序目录下或临时目录。
/r /refresh 重载键盘布局。
/remove_eng 删除顽固的英语美式键盘(不删除注册表项目)。
/caps 使用 Caps Lock 键关闭大写。
/shift 使用 Shift 键关闭大写。
/alone:0 各个窗口使用相同的输入法。
/alone:1 各个窗口使用独立的输入法。
/bar:0 关闭桌面状态栏。
/bar:1 打开桌面状态栏。
/emoji:0 表情符号面板自动关闭。
/emoji:1 表情符号面板不自动关闭。
/layout:0/1/2/3 键盘布局切换热键:0:禁用;1:Ctrl+Alt;2:Alt+Shift;3:` 。
/lang:0/1/2/3 输入语言切换热键:0:禁用;1:Ctrl+Alt;2:Alt+Shift;3:` 。
/add_num:<输入法序号> 按序号添加到输入法列表。
/add_name:<输入法名称> 按名称添加到输入法列表。
/del_num:<输入法序号> 按序号从输入法列表中禁用。
/del_name:<输入法名称> 按名称从输入法列表中禁用。
/move_num:<输入法序号> <新序号> 将指定序号的输入法移动到新位置。
/move_name:<输入法名称> <新序号> 将指定名称的输入法移动到新位置。输入法名称中如有空格,需要用双引号括起来)。
/langlist 使用语言列表替代默认输入法。
/default_num:<输入法序号> 将指定序号的输入法设置为默认输入法。
/default_name:<输入法名称> 将指定名称的输入法设置为默认输入法。
/export_enabled_list: <文件名> 导出当前使用的输入法列表。
/export_disabled_list: <文件名> 导出禁用的输入法列表。
/savecfg: <文件名> 导出配置文件。
/loadcfg: <文件名> 导入配置文件。
/hotkey_num:<输入法序号> 按序号指定输入法热键,不填写热键表示删除热键。
/hotkey_name:<输入法名称> 按名称指定输入法热键,不填写热键表示删除热键。输入法名称中如有空格,需要用双引号括起来)。
/switchkey:<a/b/c> 设置通用切换热键。a: 打开关闭输入法;b: 全角半角切换;c: 中英文标点切换。
热键格式: 分两部分,以空格分隔,左侧为控制键,右侧为字符键,如:ctrl 5、shift home、ctrl+alt d 等。
左侧控制键列表如下(不分大小写):
Alt
Ctrl
Left_Ctrl
Right_Ctrl
Ctrl+Alt
Shift
Left_Shift
Right_Shift
Alt+Shift
Ctrl+Shift
Left_Ctrl+Shift
Right_Ctrl+Shift
Ctrl+Alt+Shift

右侧字符键列表如下:
0~9、a~z、-=,.;'[]\等常规字符
? 因 / 被定义为参数的前导符,用 ? 来代表 /
~ 数字1左侧那个 ` 字符
BACKSPACE
TAB
ENTER
HOME
END
PGUP
PGDN
INS
DEL
LEFT
RIGHT
UP
DOWN
F1~F12
NUM0~NUM9 小键盘的0~9
NUM+
NUM-
NUM*
NUM? 代表小键盘的 /
NUM.
LEFT_WINDOW 左徽标键
RIGHT_WINDOW 右徽标键
APPS 应用程序键

几点说明:
一、关于热键

  1. 切换热键修改后立即生效,但只对新打开窗口有效,原有窗口仍是旧的热键有效。
  2. 切换热键可以单独选择左/右控制键,但是重启后会变回左右双键同时有效,这是 Windows 的设置功能混乱造成的,重载键盘布局即可重新生效。可以点击设置工具右下角的“刷新”按钮,也可以由命令行执行 tsftool /r 或 tsftool /refresh 重载键盘布局。
  3. TSF 类型的输入法和旧的IMM类型输入法能使用的热键范围有所不同。TSF 输入法的控制键不分左右,虽然 Windows10 的相关设置界面里显示 Alt 键只能用左 Alt,实际右 Alt 键也有效,故在命令行中指定 TSF 输入法热键时不再加 left 前缀,如:alt、ctrl+alt 等。另外,TSF输入法不能使用 Windows 键和 APPS 键。
  4. IMM输入法的控制键可单独区分左右,但只能使用 ctrl、shift、ctrl+shift这三组控制键组合,如:ctrl、left_ctrl、right_ctrl+shift 等等。
    打开/关闭输入法、全角/半角切换和中英文标点切换的热键定义范围和 IMM 输入法一致。Windows10 目前不再定义全局的全半角切换和中英文标点切换热键,但有时候也会有效,所以暂时保留了这两组切换热键设置。
  5. 因为 / 被定义为命令行参数的前导符,从命令行指定热键时使用其上档键 ? 来代替 /,小键盘的 / 用 num? 代替。
  6. 输入法热键的重码检查暂时只在当前当前语言下进行,其它语言自行注意即可。功能待以后改进。但是三组通用切换热键允许不同语言重码,互不影响。
  7. 在 Windows10 的热键设置中对“~”键的定义有误,无法生效(近年来的版本修订了这一错误),为保持兼容性本工具也保留了这个定义;另Windows10 热键设置中定义的“抑音符”实际为小键盘的数字0。
    二、关于键盘布局
  8. Windows10 从 1903 版本开始,已经无法正常将中文简体-美式键盘显示在输入法列表中了(实际是存在并启用的)。为了方便一些老用户,我在程序的输入法列表里硬加了一个“美式键盘”,以蓝色显示,可以正常使用。但是我发现,当切换到美式键盘后,是无法用打开/关闭输入法的切换热键“Ctrl+空格”切换到其它输入法的,只有改用切换键盘布局热键“Ctrl+Shift”或徽标键+空格键。我个人建议是禁用美式键盘,然后把最常用的输入法设置初始状态为“英文”,再用“Ctrl+空格”就可以正常切换了。
  9. 暂时只支持 0409 这一种英文键盘布局。
  10. 因为某些原因,在对美式键盘进行启用、禁用操作时,刷新需要 3~4 秒,注意操作不要太快。
    三、其它
  11. 所有设置修改后立即生效,没有设计“确定”、“应用”这类功能。
  12. 本工具支持 Windows10/11 32/64 位版本和 Windows Server 2019,32 位版本程序在 64 位操作系统下运行正常。Windows7/8 等以前的操作系统的设置实际差异较大,不再支持。
  13. 如果安装了多种语言的输入法,可以从底部的已安装语言列表中切换进行设置。
  14. 输入法列表中显示为“粗体” 的条目表示此输入法已设置为默认输入法,可以从列表上点击右键,从弹出菜单里修改相关设置。
  15. 每种语言下应至少保留一种输入法或键盘。
  16. 如果要添加/删除某些输入法语言,请转到操作系统的设置/时间和语言/语言下进行设置。尝试过直接删除语言,发现容易造成输入法状态混乱,暂时取消了这个功能。
  17. 偶尔会出现工具里的输入法列表和实际的输入法列表不太一致的现象,来回调整几次刷新刷新就会正常。主要原因是因为 Windows 在语言选项中的输入法列表和任务栏上的输入法列表是两个不同的配置,有时候条目数量和顺序会不一致,通过输入法工具设置会纠正这个问题。
  18. 某些输入法(如小狼毫)在卸载不完全时,TSFTool 的输入法列表里可能会存在空行,以后的版本会想办法解决这个问题( 0.9.2.1 版本已解决)。
  19. 要下载 TSFTool,可以去我的个人网站:https://www.mympc.org

             Silence
             2026年3月20日
    

Delphi 没有支持 toml 格式解析的单元,从 github 上找到了来自悉尼的 Iwan Kelaiah 写的一个 TOML Parser for Free Pascal 单元。简单修改了一下,让其支持 delphi。

  • 2025年5月,作者更新版本后已经可以支持表数组和内联表数组了。
  • 2026年1月30日,修复表数组解析异常的问题。
    原代码貌似只支持打开无签名的 utf-8 格式文件。为确保兼容性,修改为 ParseTOMLFile 函数可以打开常见编码格式的文件,如果没有签名则默认为 utf-8 格式。
    SerializeTOMLToFile 函数修改为支持签名和无签名两种 utf-8 格式(默认为有签名),兼容原始版本,即在函数的输入参数最后增加了一个默认值为 True 的 BOM 参数,来决定保存的文件有没有签名。
  • 2026年2月26日。有感于读写方式过于繁琐,设计了一个辅助单元 TOML.Helper.pas,增加了大量方法来简化使用,并修订原单元错误或增充未实现功能:如 Unicode 转义序列未实现、缺少 \b \f 转义序列、未支持行尾反斜杠、未去除多行字符串首行换行符、未处理带引号的点分隔键、未验证数字中的下划线、行尾反斜杠逻辑错误、特殊键名解析错误等。
  • 2026年3月3日。继续修订bug。增强对 TOML 规范的支持,如增加对不含“T”的日期时间格式支持等等。某些推荐性的规范尚未完全实现。
  • 2026年3月10日。完全支持 TOML V1.1.0 规范,通过了官方测试工具的全部 894 项测试(V 1.1.0 版本)。
    增加 TOML.JSON.pas 单元,支持 TOML 和 JSON 格式的相互转换。
    将组件命名为 DelphiTOML,发布在 github 上。
    刚注意到 github 上 pyscripter写的 toml-delphi,完全支持 TOML V1.0.0,有时候学习一下。
  • 2026年3月13日。支持读写注释。修订一些 bug。
  • 2026年3月18日。增加几个函数。修订一些 bug。
    下载:TOML.rar

以下说明比较简陋,详细文档参见压缩包。

新增加的用法:

  • 读取
      // 从文件加载
      Config := LoadToml('config.toml');
      // 或:
      Config := NewTable;
      Config.LoadFromFile('config.toml');
      // 基本类型
      Config.GetStr(Key, Default);       // 字符串
      Config.GetInt(Key, Default);       // 整数
      Config.GetFloat(Key, Default);     // 浮点数
      Config.GetBool(Key, Default);      // 布尔值
      Config.GetDateTime(Key, Default);  // 日期时间
      Config.GetDateTimeValue(Key, Default);  // 日期时间原始精度字符串

      Config.TryGetStr(Key, Value);      // 字符串
      Config.TryGetInt(Key, Value);      // 整数
      Config.TryGetFloat(Key, Value);    // 浮点数
      Config.TryGetBool(Key, Value);     // 布尔值
      Config.TryetDateTime(Key, Value);  // 日期时间
      Config.TryGetDateTimeValue(Key, Value);  // 日期时间原始精度字符串
      // 复杂类型
      Config.GetArray(Key);              // 数组(返回nil表示不存在)
      Config.TryGetArray(Key, value);    // 数组
      Config.GetTable(Key);              // 表(返回nil表示不存在)
      Config.TryGetTable(Key, value);    // 表

      // 数组方法
      Array.GetStr(Index, Default);
      Array.GetInt(Index, Default);
      Array.GetTable(Index);
      Array.ForEachTable(Procedure);     // 遍历数组
  • 写入

        // Set类,默认覆盖原值
        Config.SetStr(key, value, [Overwrite]);
        Config.SetInt(key, value, [Overwrite]);
        Config.SetFloat(key, value, [Overwrite]);
        Config.SetBool(key, value, [Overwrite]);
        Config.SetDateTime(key, value, [Overwrite]);
        Config.SetArray(key, value, [Overwrite]);
        Config.SetTable(key, value, [Overwrite]);
    
        // Put方法,支持重载,自动识别类型,默认覆盖原值
        Config := NewTable
          .Put(key1, value1, [Overwrite])
          .Put(key2, value2, [Overwrite])
          .Put(key3, value3, [Overwrite]);
    
        // 数组的 Add 方法
        Tags := NewArray
          .AddStr('value1')
          .AddStr('value2')
          .AddStr('value3');
    
        Ports := NewArray
          .AddInt(8080)
          .AddInt(8081)
          .AddInt(8082);
        // 另外也支持:AddFloat、AddBool、AddDateTime、AddTable
        // 按索引删除数组中数据
        Array.RemoveAt(idx);
        // 清空数组
        Array.Clear;
    
        // 更新或创建函数(功能重复,已删除)
    
        // 创建表或数组
        Config := NewTable;
        Tags := NewArray;
        // 或更短的别名
        Config := Table;
        Tags := Arr;
    
        // 其它工具方法
        Config.ToString;                 // 转换为字符串
        Config.Count;                    // 获取键数量
        Config.HasKey(Key);              // 检查键是否存在
        Config.GetKeys(List, Recursive); // 获取所有键名
        Config.REmove('key');            // 删除键
  • 示例:

      //打开 toml 文件
      Config := LoadToml('config.toml');
      // 或:
      Config := NewTable;
      Config.LoadFromFile('config.toml');
      // 读取
      width := Config.GetInt('width', 800);
      title := Config.GetStr('title');
      debug := Config.GetBool('debug',False);
      name := Config.GetStr('server.host', 'localhost');  // 支持点分隔路径
    
      //创建
      Config := NewTable;
      Tags := NewArray;
    
      //创建同时赋值:
      Config := NewTable
        .SetStr('app_name', 'My Application')
        .SetStr('version', '1.0.0')
        .SetInt('port', 8080)
        .SetBool('debug', False);
      Tags := NewArray
        .AddStr('pascal')
        .AddStr('delphi')
        .AddStr('toml');
    
      //创建嵌套结构:
      Config := NewTable
        .Put('server',
          NewTable
            .Put('host', 'localhost')
            .Put('port', 8080)
        )
        .Put('database',
          NewTable
            .Put('host', 'localhost')
            .Put('port', 5432)
            .Put('pool_size', 10)
        );
    
      // 写入(默认覆盖原值)
      Config.SetStr('title', 'My App');
      Config.SetInt('width', 1920, True);
      Config.SetBool('debug', False);
      Config.SetFloat('version', 1.1, False);
    
      // 自动识别类型写入(默认覆盖原值)
      Config.Put('width', 1920)
            .Put('height', 1080)
            .Put('title', 'My App', False);
      // 保存文件
      Config.SaveToFile('config.toml');
    
      //创建表数组
      Servers := NewArray
        .AddTable(
          NewTable
            .Put('name', 'web-01')
            .Put('ip', '192.168.1.10')
        )
        .AddTable(
          NewTable
            .Put('name', 'db-01')
            .Put('ip', '192.168.1.20')
        );
      Config.SetArray('servers', Servers);
    
      // 遍历数组方式一
      parameters.ForEachTable(
        procedure(param: TTOMLTable)
          begin
            showmessage(param.GetStr('name'));
          end
      );
      // 遍历数组方式二
      procedure ProcessParameter(param: TTOMLTable);
        begin
          showmessage(param.GetStr('name'));
        end;    
      parameters.ForEachTable(ProcessParameter);  // 调用

以下内容为翻译的一些原始用法:

config.toml 示例:

# 注释:config.toml

revision = "1.2.1af"

[project]
name = "My Amazing Project"
version = "1.0.0"

基本用法

  • 读取 TOML 文件
    program BasicParseTOML;
    uses
      TOML;    
    var
      Config: TTOMLTable;
      RevisionValue, ProjectValue, ProjectName: TTOMLValue;
      ProjectTable: TTOMLTable;    
    begin
      // 打开 TOML 文件
      Config := ParseTOMLFromFile('config.toml');
      try
        // 访问字符串类型数据
        if (Config.TryGetValue('revision', RevisionValue)) then
          WriteLn('''revision'' 的值为:', RevisionValue.AsString);   
        // 安全地访问数据
        if Config.TryGetValue('project', ProjectValue) and
          (ProjectValue is TTOMLTable) then
        begin
          ProjectTable := TTOMLTable(ProjectValue);
          if ProjectTable.TryGetValue('name', ProjectName) then
            WriteLn('Project Name: ', ProjectName.AsString)
          else
            WriteLn('Project name 未找到。');
        end
        else
          WriteLn('Project 配置未找到。');
      finally
        Config.Free;
      end;
    end.
  • 写入 TOML 文件
    program BasicSerializeTOML;
    uses
      TOML;
    var
      Config: TTOMLTable;
      Database: TTOMLTable;
    begin
      Config := TOMLTable;
      try
        Database := TOMLTable;
        // 添加数据
        Database.Add('host', TOMLString('localhost'));
        Database.Add('port', TOMLInteger(5432));
        // 修改数据
        Database.Items.AddOrSetValue('enable',TOMLBoolean(True));
        Config.Add('database', Database);    
        if SerializeTOMLToFile(Config, 'config.toml') then
          WriteLn('配置保存成功。')
        else
          WriteLn('保存配置出错。');
      finally
        Config.Free;
      end;
    end.

常见用法

  • 使用数组
    var
      Config: TTOMLTable;
      Tags: TTOMLArray;
    begin
      Config := TOMLTable;
      try
        Tags := TOMLArray;
        Tags.Add(TOMLString('pascal'));
        Tags.Add(TOMLString('toml'));
        Config.Add('tags', Tags);    
        WriteLn(SerializeTOML(Config));
      finally
        Config.Free;
      end;
    end.
  • 嵌套表
    var
      Config: TTOMLTable;
      Database: TTOMLTable;
    begin
      Config := TOMLTable;
      try
        Database := TOMLTable;
        Database.Add('host', TOMLString('localhost'));
        Database.Add('port', TOMLInteger(5432));
        Config.Add('database', Database);
        WriteLn(SerializeTOML(Config));
      finally
        Config.Free;
      end;
    end.
  • 序列化复杂结构
    program BasicSerializeTOML;
    uses
      TOML, SysUtils;
    var
      Config, ServerConfig: TTOMLTable;
      Ports: TTOMLArray;
      SerializedTOML: string;
    begin
      Config := TOMLTable;
      try
        // 创建嵌套表
        ServerConfig := TOMLTable;
        ServerConfig.Add('host', TOMLString('127.0.0.1'));
        ServerConfig.Add('enabled', TOMLBoolean(True));
        // 创建和填充数组
        Ports := TOMLArray;
        Ports.Add(TOMLInteger(80));
        Ports.Add(TOMLInteger(443));
        ServerConfig.Add('ports', Ports);    
        // 添加 server 配置到主配置
        Config.Add('server', ServerConfig);    
        // 添加一些基础元数据
        Config.Add('version', TOMLFloat(1.0));
        Config.Add('last_updated', TOMLDateTime(Now));    
        // 序列化为 TOML 格式
        SerializedTOML := SerializeTOML(Config);
        WriteLn('生成 TOML:');
        WriteLn(SerializedTOML);    
        // 保存文件
        if SerializeTOMLToFile(Config, 'config.toml') then
          WriteLn('保存成功。');    
      finally
        Config.Free;
      end;
    end.

上述代码将生成 TOML 文件,内容如下:

    version = 1.0
    last_updated = 2024-03-20T15:30:45Z
    [server]
    host = "127.0.0.1"
    enabled = true
    ports = [ 80, 443 ]

说明:所有数据都经过适当的类型检查和内存管理。该单元可确保:

  • 每个值都有正确的 TOML 类型
  • 数组保持类型一致性
  • 所有对象都已正确释放
  • 验证类型转换
  • 内存管理

注意:

  • 仅释放顶级 owner 表以避免内存管理问题。
  • 适当的内存管理对于防止应用程序中的内存泄漏至关重要。

创建/释放表:

    var
      Config: TTOMLTable;
      Database: TTOMLTable;
    begin
      Config := TOMLTable;
      Database := TOMLTable;
      //使用 Add 方法将嵌套表或值插入父表中。
      Config.Add('database', Database);
      // ... 添加其它嵌套表及数据...
      Config.Free; // 仅释放顶级所有者即可自动释放所有嵌套的表和值。
    end.

避免显式释放嵌套对象:
• 不要手动释放嵌套表或值,以防止出现内存管理问题。

API 参考

类型

  • TTOMLValue - 所有 TOML 值的基本类型
  • TTOMLTable - TOML 表
  • TTOMLArray - TOML 数组
  • TTOMLString - TOML 字符串类型
  • TTOMLInteger - TOML 整数类型
  • TTOMLFloat - TOML 浮点类型
  • TTOMLBoolean - TOML 布尔类型
  • TTOMLDateTime - TOML 日期时间类型

用于创建 TOML 值的辅助函数

创建 TOML 字符串值。

    TOMLString
      function TOMLString(const AValue: string): TTOMLString;

创建 TOML 整数值。

    TOMLInteger
      function TOMLInteger(const AValue: Int64): TTOMLInteger;

创建 TOML 浮点值。

    TOMLFloat
      function TOMLFloat(const AValue: Double): TTOMLFloat;

创建 TOML 布尔值。

    TOMLBoolean
        function TOMLBoolean(const AValue: Boolean): TTOMLBoolean;

创建 TOML 时间日期格式。

    TOMLDateTime
        function TOMLDateTime(const AValue: TDateTime): TTOMLDateTime;

创建 TOML 数组。

    TOMLArray
      function TOMLArray: TTOMLArray;

创建 TOML 表。

    TOMLTable
      function TOMLTable: TTOMLTable;

解析函数

  • ParseTOML
    将 TOML 格式的字符串解析为 TTOMLTable 对象。
  function ParseTOML(const ATOML: string): TTOMLTable;
  begin
    Result := TOML.Parser.ParseTOMLString(ATOML);
  end;

• ParseTOMLFromFile
将 TOML 文件解析为 TTOMLTable 对象。

      function ParseTOMLFromFile(const AFileName: string): TTOMLTable;
      var
        FileStream: TFileStream;
        StringStream: TStringStream;
      begin
        FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
        try
          StringStream := TStringStream.Create('');
          try
            StringStream.CopyFrom(FileStream, 0);
            Result := ParseTOMLString(StringStream.DataString);
          finally
            StringStream.Free;
          end;
        finally
          FileStream.Free;
        end;
      end;

序列化函数

  • SerializeTOML
    将 TTOMLValue 序列化为 TOML 格式的字符串。

      function SerializeTOML(const AValue: TTOMLValue): string;
      begin
        Result := TOML.Serializer.SerializeTOML(AValue);
      end;

• SerializeTOMLToFile

序列化 TTOMLValue 并将其保存为文件(BOM:是否为带签名的 UTF-8 编码格式,默认有签名)。

      function SerializeTOMLToFile(const AValue: TTOMLValue; const AFileName: string; BOM: Boolean = True): Boolean;
      begin
        Result := TOML.Serializer.SerializeTOMLToFile(AValue, AFileName; BOM: Boolean = True);
      end;

以前用了插件,有人留言时会发邮件给我,最近发现开始不发邮件了。懒得仔细研究,就换了个 Robotpush 插件,是可以发钉钉、微信等机器人消息的。结果消息我是收到了,人家的留言却发不出去。
偷懒不得,看了一下源码,在第 199 行:

self::sendRobotMessages($robotTypes, $dingtalkWebhookUrl, $feishuWebhookUrl, $wecomWebhookUrl, $message);

之后加入一行:

return $comment;

测试成功。
另一处同样代码后加了一行 return $user;