分类 编程 下的文章

以前写过的一个映射键盘按键的小工具,应人之邀再放上来。这是个64位版本的工具,以前32位的那个没什么必要再上传了。
2023年9月9日更新,修订了网友提出的捕捉按键时的bug,并优化了相关流程。
keybmap_x64.rar
KeybMap.png

钉钉的机器人群消息很好用,也不用申请特别的权限,免费的消息条数也很多,做个监控类的消息通知很合适。一般都是用 python、JAVA 等语言来编写调用代码,简单使用也可以直接命令行调 curl。
这几天想给一个用 delphi 编写的运维工具加上钉钉消息功能。尝试编写了一下,用自定义关键字方式发送消息很简单,一次就通过了;然而加签方式却死活通不过,总是返回加签错误。仔细阅读了N遍官方文档,就是个很常用的 HMAC-SHA256 + Base64 加签算法。蹊跷的是,我换了三四种不同的代码去实现,每一种算法得到的结果和网上的在线计算器的结果都一模一样,然而就是和官方的 Python 语言例程的结果不一样。
晚饭后出门散步时我继续思考这个问题:既然我的代码和在线计算器的一致,说明算法本身没有错误,那么只能是输入参数不一致;这时我突然领悟到,Python 和 JAVA 语言都会自动处理 \n 这样的转义字符串而 Delphi 不会,官方文档里要求在加签字符串中添加了一个 \n,肯定就是这里导致的错误。后来在代码中用 #10 来代替 \n,果然验签通过。
那么就分享一段完整的用 delphi 发送钉钉机器人消息的代码吧:

unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  System.JSON, Vcl.StdCtrls, Hash, System.Net.URLClient, DateUtils,
  System.Net.HttpClient, System.Net.HttpClientComponent, NetEncoding;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    NetHTTPClient1: TNetHTTPClient;
    function dingtalk(content: string): boolean;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
  dingtalk('test,测试。[关键字]');
end;

function TForm1.dingtalk(content: string): boolean;
//钉钉机器人消息发送函数
//URL 格式:https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&timestamp=XXX&sign=XXX
var
  url, keywords, token, secret, timestamp, sign, str: string;
  s: TStringStream;
  json: TJSONObject;
begin
  //result := false;
  begin
    NetHTTPClient1.ContentType := 'application/json';
    NetHTTPClient1.AcceptCharSet := 'utf-8';
    NetHTTPClient1.AcceptLanguage := 'zh-CN';
    token := 'xxxxxxxx';
    keywords := '';
    secret := 'SECxxxxxxxx';
    url := 'https://oapi.dingtalk.com/robot/send?access_token=' + token;
    (*
    发送消息有两种安全策略,自定义关键字和加签,至少选择一种,也可同时使用。
    secret 为加签密钥,不为空时表示需要加签。
    timestamp 为毫秒级时间戳。
    加签算法是常用的 HmacSHA256 + Base64:
    把 timestamp+"\n"+secret 当做签名字符串,使用HmacSHA256算法计算签名,
    然后进行 Base64 编码,将得到的结果再进行 urlEncode 编码,从而得到最终签名数据 sign。
    *)
    if secret <> '' then
    begin
      //获取毫秒级时间戳
      timestamp := (MilliSecondsBetween(Now, EncodeDateTime(1970, 1, 1, 0, 0, 0, 0)) - 8 * 60 * 60 * 1000).ToString;
      //加签
      sign := TNetEncoding.Base64.EncodeBytesToString(THashSHA2.GetHMACAsBytes(timestamp + #10 + secret, secret, SHA256));
      //URLEncode 编码
      sign := TNetEncoding.url.Encode(str);
      //拼接 url
      url := url + '&timestamp=' + timestamp + '&sign=' + sign;
    end;
    //keywords 为自定义关键字,若为空时则必须加签,即 secret 不能为空。
    if keywords <> '' then
      keywords := keywords + '\n';
    //拼接 json 格式的消息内容字符串,如有自定义关键字,可插入到内容的任意位置。
    s := TStringStream.Create('{"msgtype":"text","text": {"content":"' + keywords + content + '"}}', TEncoding.UTF8);
    //发送 post 请求
    try
      str := NetHTTPClient1.Post(url, s).ContentAsString(nil);
    except
      on E: Exception do
        memo1.Lines.Add('发送消息时出错:' + e.Message);
    end;
    //解析返回结果
    try
      json := TJSONObject.ParseJSONValue(str, true, true) as TJSONObject;
      if json.GetValue<integer>('errcode') = 0 then
      begin
        result := true;
        memo1.Lines.Add('消息发送成功。');
      end
      else
      begin
        result := false;
        memo1.Lines.Add('消息发送失败,错误代码:' + json.GetValue<string>('errcode') + json.GetValue<string>('errmsg'));
      end;
      s.Free;
      json.Free;
    except
      on E: Exception do
      begin
        result := false;
        memo1.Lines.Add('发送消息时出错:' + e.Message);
      end;
    end;
  end;
end;

end.

最近偶然需要用一下 Delphi 下的 Raize,使用时报 rzborder.pas 错误。记忆中多少年前就知道这是个编码识别的问题。放狗一搜,居然现在有人说是RZ的源码有问题,不能指向RZ的源码;还有人说是源码缺失},辛辛苦苦地去一行行修改,把类似下面这种:

( { Col0: $00; Col1: $7E; Col2: $14; Col3: $08; Col4: $00 ),

改为:

( {} Col0: $00; Col1: $7E; Col2: $14; Col3: $08; Col4: $00 ),

实际上人家的源码没有任何错误,只是在中文系统下 Delphi 使用默认的GB2312编码格式把特殊字符+}错误地识别成了汉字而已。
解决方法很简单:用 Emeditor 以西欧 Windows-1252 编码页打开 rzborder.pas 文件,然后另存为带签名的 utf-8 编码格式即可。你会发现上面那行代码其实是这样的:

( {þ} Col0: $00; Col1: $7E; Col2: $14; Col3: $08; Col4: $00 ),

那个特殊字符 þ 的 16 进制编码是 FE,它后面的 } 是 7D,而汉字“”的 GB2312 编码恰好就是 16 进制的 FE7D。

imetool.rarIMETool 最后一个版本是 2.8.3,更新于2010年1月15日,存档一个以作纪念。注意:只支持 XP 及以前版本,不支持现在流行的 Windows 10/11 等操作系统。
imetool.png

声明:IMETool 为免费软件,非商业使用无须授权,但不得对软件本身做任何修改。允许个人开发的免费输入法软件将其加入安装包使用,但安装包内不得包含有广告插件或流氓软件。

IME TOOL 支持中英文界面,适用于 Win98/ME/2000/XP/2003 操作系统,部分兼容 Windows 7。
主要功能:
一、动态调整输入法顺序而无须重启。
二、切换输入法指示器和高级文字服务方式。
三、任意设置输入法热键
Windows 本身的输入法热键设置功能限制很多,2000/XP 下更是如此,你不得不设置 ctrl+shift+3 这样的三键热键。IME Tool 让你可以自由地设置各种热键,基本键(ctrl、alt、shift)共有六种组合,辅助键有 91 个可选,如用左 Alt+左 Window 键激活龙文五笔,shift+down 激活紫光拼音。当然,有些热键组合是不能用的,如 shift+数字字母键。
四、注册表错误项目修正
1、自动删除输入法列表中不存在的输入法项目,位置:
HKEY_CURRENT_USER\Keyboard Layout\preload
2、检查注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts
下的内容,如果某分支对应的输入法文件不存在,则删除此分支(需要管理员权限)。
3、自动删除输入法从列表中删除后可能遗留的激活热键设置,位置:
HKEY_CURRENT_USER\Control Panel\Input Method\Hot Keys
五、输入法键盘定义功能,适用于非英文键盘(需要管理员权限)。
六、安装删除输入法
IME TOOL 可以安装标准格式的输入法。自行复制 ime 和 mb 文件到系统目录中,然后使用 imetool 的安装输入法功能指定输入法名称和 ime 文件即可。
IME TOOL 可以从注册表中删除不需要的输入法,但并不删除文件。
七、其它功能
打开输入法属性、打开关闭输入法状态条等。支持命令行参数,支持运行脚本。
另外修正了老版本存在的一些小 bug。

局限:
1、在 Win9x 下设置的输入法热键不能立即生效,必须重启。在 Win95/NT 系统下有更多的功能限制。

注意:
1、用 IME Tool 设置的一些热键在 Windows 本身的输入法设置中会无法正确显示,但不影响使用。
2、繁体、韩文、日文系统下的打开关闭输入法等切换键设置在注册表中的位置不同,因条件所限我没有做进一步测试功能是否有效。
3、在 IME Tool 中刚添加的输入法不能立即设置属性。
4、如果使用输入法指示器方式时,在系统目录下未发现 internat.exe 和 indicdll.dll 文件,将会自动复制当前目录下的此二文件过去(如果存在)。

命令行参数说明:

/? /h /help 显示此帮助
/on 启用 internat.exe 或 ctfmon.exe
/off 禁用 internat.exe 或 ctfmon.exe
/internat 输入法指示器风格
/ctfmon 高级文字服务风格
/status:<0/1> <0>: 关闭输入法状态条,<1>: 显示输入法状态条
/caps 用 Caps 键关闭大写
/shift 用 Shift 键关闭大写
/switch:<0/1/2/3><a/b> <0>: 不使用切换语言和切换键盘布局热键

                <1>: 仅使用切换语言热键
                <2>: 仅使用切换键盘布局热键
                <3>: 同时使用切换语言和切换键盘布局热键
                <a>: 切换语言热键使用Ctrl+Shift
                <b>: 切换键盘布局热键使用Ctrl+Shift

/hotkey:<1/2/3/a/b/c>,[hotkey] 指定输入法热键

                <1/2/3..>,指定第1、2、3.. 个输入法的热键
                <a> 指定打开/关闭输入法的热键
                <b> 指定全角/半角切换的热键
                <c> 指定中英文标点切换的热键
                如果留空则删除热键
                [hotkey] 热键值,如:ctrl+space

/add1:,[hotkey] 按名称添加输入法到列表
/add2:,[hotkey] 按文件名添加输入法到列表
/add3:,[hotkey] 按 ID 添加输入法到列表
/del1:<1/2/3> 按顺序从列表中删除输入法
/del2: 按名称从列表中删除输入法
/del3: 按文件名从列表中删除输入法
/del4: 按 ID 从列表中删除输入法
/install:[name],,[hotkey] 安装输入法
/uninstall1: 按名称从注册表删除输入法
/uninstall2: 按文件从注册表删除输入法
/uninstall3: 按 ID 从注册表删除输入法
/move1:, 按名称调整输入法顺序, 为调整后的序号,下同
/move2:, 按文件名调整输入法顺序
/move3:, 按 ID 调整输入法顺序
/load: 从配置文件调入参数

[hotkey] 格式: Ctrl+Shift_8_Left,如果同时使用左右键,则可以写为:
Ctrl+Shift_8。
注意:要在命令行参数设置“/”和“,”作为辅助热键时,为避免和参数前导符和分隔符冲突,应写为“?”和“~”,如:ctrl_?_right、alt_~
要安装一个输入法到注册表时,须手工复制文件,xx.ime 文件必须复制到系统目录中。使用 /install 参数安装时,xx.ime 文件名前不能加路径,如:
imetool /install:中文(简体) - 智能 ABC,winabc.ime

欢迎测试并反馈 bug。

更新:
2.8.3 版,去除安装输入法时多余的弹出窗口(调试程序用,以前版本忘记屏蔽)。
2.8.2 版,兼容 Windows 7,但存在如下问题:输入法顺序调整功能不正常,安装输入法功能有问题暂时禁用。另外关闭了输入法指示器模式。
2.8.1 版,修正网友提出的在命令行无法设置“,”热键问题(改用“~”符号),主程序增加版本信息资源。为避免一些弱智杀毒软件误报病毒的问题,不再使用 upx 压缩,体积由 50K 增加至 119K。
2.8.0 版,修正“APPS”(右键菜单键)热键误设为右 Windows 键的问题。
2.7.9 版,修正热键选择为“无”,确定后程序非法操作的问题。
2.7.8 版,修正在输入法风格里连续点击同一个模式单选框时,输入法切换热键会来回切换问题。
2.7.7 版,修正 /off 参数无效问题;修正 /del1 参数指定了不存在的序号时出错的问题。
2.7.6 版,改进显示某些输入法名称不正确的问题(如微软拼音2007),6月24日增补一处界面中的繁体提示信息,未更新版本号。
2.7.5 版,修订 2.7.2 版出现的在某些情况下输入法列表显示错位的情况。
2.7.4 版,增加繁体显示界面;修改 internat.exe 和 ctfmon.exe 在注册表中的值,以和系统保持一致。
2.7.3 版,修订子窗口的一些问题。
2.7.2 版,增强对 VISTA 的兼容性;改进输入法列表中输入法名称的显示方式;增加自定义输入法名称功能(仅对高级文字服务模式有效,点击列表中输入法名称或按 F2 进行修改,如应用后看不到效果,注销或重启即可);修订在 2003 下列表可能不显示的问题,修订一些 bug。
2.7.1 版,修订重复热键可能无法取消的 bug。
2.7.0 版,修订热键重复提示的 bug。
2.6.9 版,增加一种修复注册表重复项目功能。
2.6.8 版, 增加三个命令行参数,用以调整输入法顺序。改善输入法状态条显示问题。增加一些状态条显示信息。帮助文档改用 utf-8 格式保存。
2.6.7 版,修订上一版本在 2003 下无法正常显示输入法列表的问题(此问题是由我使用的新版本控件bug引起)。
2.6.6 版,增加两个命令行参数,修订以命令行方式添加输入法时在某些情况下添加多余输入法的问题。
2.6.5 版,使高级文字服务和输入法指示器风格下的输入法切换热键保持一致,文字提示稍做调整,便于用户理解。增加常见问题说明。
2.6.4 版,程序内部一些调整。
2.6.3 版,程序内部一些调整。
2.6.2 版,解决某些情况下可能会使输入法指示条消失的问题(注:经仔细测试,此问题并未解决,具体原因不明,待研究)。
2.6.1 版,实现了带参数运行时完全隐藏主界面。
2.6.0 版,修复使用 /install 参数时在某种情况下会运行出错的 bug。参数中的分隔符由竖线改为逗号。
2.5.9 版,将自动复制 internat.exe/indicdll.dll 的源路径由当前路径改为 imetool.exe 所在路径。在程序左上角菜单中增加命令行参数提示。
2.5.8 版,修改键盘定义对话框丢失下拉列表问题(真是莫名其妙),一些细节调整。
2.5.7 版,修订其它切换热键的设置按钮文字不会随左边的下拉列表切换的 bug。
2.5.6 版,应网友要求取消气球提示,改为状态栏提示。
2.5.5 版,增强对语言栏丢失时的修复功能(有时需要注销后方可生效)。
2.5.4 版,修订设置热键时小键盘某些键值不对的问题。
2.5.3 版,修订启动时检测输入法风格可能不对的 bug。
2.5.2 版,本修订添加输入法时图标显示的一处 bug。高级/从注册表删除输入法时可在输入法显示框中用鼠标滚轮切换不同的输入法。
2.5.1 版,修订一些程序上的 bug,添加关于信息。
2.5.0 版,支持命令行参数、支持从脚本调入参数运行。
2.4.5 版,修订安装输入法时导致出错的问题。
2.4.4 版,继续修订 bug。
2.4.3 版,修正 2.4.2 版未完全修正的问题。
2.4.2 版,修正点击应用按键后一些显示状态错位问题。
2.4.1 版,修正热键设置窗口状态显示不正常问题。
2.4.0 版,重新调整界面布局。增加应用按钮。安装 ime 文件时可以自动获取文件信息做为输入法名称。调整程序处理流程。
2.3.1 版,更新:大写关闭键设置后可立即生效;在输入法列表的不同列上双击可分别打开属性和热键设置。压缩包中的 internat.exe 做了修改,在其右键上选择属性可打开 imetool。另外修订一些 bug。
2.3.0 版,增加安装删除输入法功能;增加一些项目的浮动提示;增加可安装输入法(语言)的范围;修正一些小的 bug。改变版本号的命名方式便之规范化。
2.22 版,一些内部项目的调整
2.21 版,修订会误删除某些注册表项目的 bug。
2.2 版,可以将排列第一位的输入法立即设为默认输入法而无须重启。
2.12 版,修正输入法打开关闭等热键无法取消的 bug。
2.11 版,修正某些情况下程序会无法运行的 bug。

本压缩包中附带的 internat.exe 和 indicdll.dll 为从 Windows 2000 Pro 简体中文版中提取并修改过,适用于 XP/2003。

                    Silence
                    2010.1.15