找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 828|回复: 14

办公WPS的JSA中操作剪贴板文本

[复制链接]
发表于 2026-3-24 16:59:58 | 显示全部楼层 |阅读模式
本帖最后由 cutebe 于 2026-3-30 10:33 编辑

使用方法       //JS代码,WPS的JSA宏
  1. var 剪贴板文本=获取剪贴板文本();
  2. var 修改文本="新文本:\n"+ 剪贴板文本 +"\n字符串结束!";
  3. 设置剪贴板文本(修改文本);        //新文本放入剪贴板,可粘贴使用
复制代码


//在WPS中使用ExecuteExcel4Macro宏调用CALL,限制字符串类型的参数限长255字节。
//一个汉字占2个字节        //参数为字符串时需要引号包起,且类型必须用F。
//let 文本字节长度=执行宏调用(`CALL("kernel32","lstrlenA","JF","${文本内容}")`);
//使可以处理双引号"而不提示错误
//let 文本字节长度=执行宏调用(`CALL("kernel32","lstrlenA","JF","${文本内容.replace(/["]/g,'x')}")`);
//let 文本字节长度=执行宏调用(`CALL("kernel32","lstrlenA","JF","${文本内容.replace(/\"/g,'y')}")`);
//let 文本字节长度=执行宏调用(`CALL("kernel32","lstrlenA","JF","${文本内容.replace(/"/g,'z')}")`);
//参数为(字符串所在的)内存地址时不限制
//let 文本字节长度=执行宏调用(`CALL("Kernel32","lstrlenA","JJ",${内存地址})`);

设置剪贴板文本("字符串");
  1. function 设置剪贴板文本(文本内容){    //参数为字符串
  2.     let 执行宏调用=ExecuteExcel4Macro;    //调用系统应用接口。简化书写
  3.     let 分段文本=[],段长度=127,分段字节=[],总字节=0;    //去掉结束符\0,双字节文字最长127
  4.     for(let 起始=i=0;起始<文本内容.length;起始+=段长度,i++){
  5.         分段文本.push(文本内容.slice(起始,起始+段长度));
  6.         分段字节.push(分段文本[i].replace(/[^\x00-\xff]/g,'xx').length);    //计算字节数
  7.         总字节+=分段字节[i];
  8.     }    //console.clear();    //清空立即窗口(控制台)信息
  9.     if (执行宏调用(`CALL("user32.dll","OpenClipboard","AJ",0)`)){        //打开剪贴板
  10.         if(执行宏调用(`CALL("user32.dll","EmptyClipboard","A")`)){    //清空剪切板
  11.             const 内存句柄=执行宏调用(`CALL("Kernel32","GlobalAlloc","JJJ",${0x42},${总字节+1})`);    //分配内存
  12.             //console.log("内存块大小:"+执行宏调用(`CALL("Kernel32","GlobalSize","JJ",${内存句柄})`));
  13.             if(内存句柄){    //内存分配成功。然后锁、写、解内存。最后设置剪贴板。
  14.                 const 内存地址=执行宏调用(`CALL("kernel32.dll","GlobalLock","JJ",${内存句柄})`);    //锁定对象,返回地址
  15.                 for(let 偏移字节=i=0;i<分段文本.length;i++){    //字符串(JS变量)分批复制到内存中。分批是因为有限长
  16.                     执行宏调用(`CALL("Kernel32","lstrcpynW","JJFJ",${内存地址+偏移字节},"${分段文本[i].replace(/"/g,'""')}",${分段字节[i]})`);
  17.                     偏移字节+=分段字节[i];    //返回地址J。
  18.                 }
  19.                 执行宏调用(`CALL("kernel32.dll","GlobalUnlock","JJ",${内存句柄})`);    //解锁全局内存对象
  20.                 let 剪贴板文本=执行宏调用(`CALL("user32.dll","SetClipboardData","JJJ",1,${内存句柄})`);
  21.                 执行宏调用(`CALL("Kernel32","GlobalFree","JJ",${内存句柄})`);    //释放内存
  22.             }else{console.log("未能分配内存!");}    //数据放入剪切板,参数为1(CF_TEXT)则返回文本
  23.         }else{console.log('未能清空剪贴板!');}
  24.         执行宏调用(`CALL("user32.dll","CloseClipboard","A")`);    //关闭剪贴板,以释放控制权。
  25.     }else{alert('无法打开剪贴板!');}
  26. }
复制代码


var 字符串=获取剪贴板文本();
  1. function 获取剪贴板文本(){    //返回字符串
  2.     let 内存地址,剪贴板文本="",执行宏调用=ExecuteExcel4Macro;
  3.     if(执行宏调用(`CALL("user32.dll","OpenClipboard","JJ",0)`)){    //打开剪贴板
  4.         const 内存句柄=执行宏调用(`CALL("user32.dll","GetClipboardData","JJ",1)`);    //获取剪贴板文本,返回句柄。
  5.         if(内存句柄){    //锁定全局内存对象,获取内存地址
  6.             内存地址=执行宏调用(`CALL("kernel32.dll","GlobalLock","JJ",${内存句柄})`);
  7.             let 总字节,偏移字节=0,段长度=127,分段文本='';
  8.             总字节=执行宏调用(`CALL("Kernel32","lstrlenA","JJ",${内存地址})`);    //剪贴板文本字节数
  9.             while(偏移字节<总字节){    //返回字符串F。返回值超255字节时,也需要分段操作
  10.                 分段文本=执行宏调用(`CALL("Kernel32","lstrcpynW","FFJJ","",${内存地址+偏移字节},${段长度})`);
  11.                 偏移字节+=分段文本.replace(/[^\x00-\xff]/g,'xx').length;    //计算分段文本字节数
  12.                 剪贴板文本+=分段文本;
  13.             }
  14.             执行宏调用(`CALL("kernel32.dll","GlobalUnlock","JJ",${内存句柄})`);    //解锁
  15.         }else{console.log("未能获取剪贴板数据的内存句柄!");}
  16.         执行宏调用(`CALL("user32.dll","CloseClipboard","J")`);    //关闭剪贴板,让其它应用可以使用剪贴板
  17.         return 剪贴板文本;    //返回字符串
  18.     }else{alert('无法打开剪贴板!');}
  19. }
复制代码


更新:2026-03-29
一、可处理双引号。原来有引号会提示错误并退出;.replace(/"/g,'""')
二、能处理超255字节文本。原字节数与字符串长度计算不匹配;
三、合并代码与简化注释。
 楼主| 发表于 2026-3-24 17:08:42 | 显示全部楼层
本帖最后由 cutebe 于 2026-3-30 15:32 编辑

适用于WPS支持ExecuteExcel4Macro的旧版本(还没有FFI与ffi的版本)。

执行ExecuteExcel4Macro宏调用时,字符串参数要加双引号包起:"${字符串变量}"。这样当遇到字符串中有双引号时就会出错。
宏调用时会(按VBA的语法)将字符串中两个双引号解释成一个,所以写入时反过来替换即可:"${字符串.replace(/["]/g,'\"\"')}"

以下字符串到内存,内存到字符串。能处理引号,不限于255字节。
  1. function 字符串内存测试(){
  2.     let 字符串="字符串内存测试16\"字符串内存\"测试32\"字符串内存测试48字符串内存测试64";
  3.     let 新字符串='';
  4.     for(let i=0;i<4;i++){新字符串+=字符串+"\n";}
  5.     新字符串+=字符串;
  6.     const 内存句柄=字符串到内存(新字符串);
  7.     let 取得字符串=内存取字符串(内存句柄)+"\n结束!";
  8.     ExecuteExcel4Macro(`CALL("Kernel32","GlobalFree","JJ",${内存句柄})`);    //释放内存
  9.     console.clear();console.log(取得字符串);
  10. }

  11. function 字符串到内存(字符串){    //返回内存句柄
  12.     const 执行宏调用=ExecuteExcel4Macro;
  13.     let 分段文本=[],分段字节=[],总字节=0,分段长=127;    //可申请256字节内存,结束符1字节,汉字最长127
  14.     for(let 起始=i=0;起始<字符串.length;起始+=分段长,i++){
  15.         分段文本.push(字符串.slice(起始,起始+分段长));
  16.         分段字节.push(分段文本[i].replace(/[^\x00-\xff]/g,'xx').length);    //一个汉字占两个字节
  17.         总字节+=分段字节[i];
  18.     }    //分配内存    //分配GlobalAlloc对应销毁GlobalFree    //分配LocalAlloc对应销毁LocalFree
  19.     const 内存句柄=执行宏调用(`CALL("Kernel32","GlobalAlloc","JJJ",${2},${总字节+1})`);
  20.     if(内存句柄){    //内存分配成功,然后锁写解内存。    //锁定全局内存对象,会返回其内存地址
  21.         const 内存地址=执行宏调用(`CALL("kernel32.dll","GlobalLock","JJ",${内存句柄})`);
  22.         for(let 偏移字节=i=0;i<分段文本.length;i++){    //字符串(JS变量)分批复制到刚申请的内存中。分批是因为有限长
  23.             执行宏调用(`CALL("Kernel32","lstrcpynW","JJFJ",${内存地址+偏移字节},"${分段文本[i].replace(/\"/g,'\"\"')}",${分段字节[i]})`);
  24.             偏移字节+=分段字节[i];    //返回地址J。    //字符串中含双引号出错,因此替换成单引号
  25.         }
  26.         执行宏调用(`CALL("kernel32.dll","GlobalUnlock","JJ",${内存句柄})`);    //解锁全局内存对象,以释放资源
  27.         return 内存句柄;
  28.     }else{console.log("未能分配内存!");}
  29. }

  30. function 内存取字符串(内存句柄){    //返回字符串
  31.     const 执行宏调用=ExecuteExcel4Macro,段长度=127;
  32.     const 内存地址=执行宏调用(`CALL("kernel32.dll","GlobalLock","JJ",${内存句柄})`);
  33.     if(内存地址){
  34.         const 总字节=执行宏调用(`CALL("Kernel32","lstrlenA","JJ",${内存地址})`);    //内存文本字节数
  35.         let 偏移字节=0,文本字符串=分段文本='';
  36.         while(偏移字节<总字节){    //返回字符串F。    //返回超255字节时,也需要分段操作
  37.             分段文本=执行宏调用(`CALL("Kernel32","lstrcpynW","FFJJ","",${内存地址+偏移字节},${段长度})`);
  38.             //偏移字节+=执行宏调用(`CALL("Kernel32","lstrlenA","JC","${分段文本.replace(/["\n]/g,'x')}")`);
  39.             偏移字节+=分段文本.replace(/[^\x00-\xff]/g,'xx').length;    //计算字符串字节数
  40.             文本字符串+=分段文本;
  41.         }    //解锁全局内存对象,使内存可以回收
  42.         执行宏调用(`CALL("kernel32.dll","GlobalUnlock","JJ",${内存句柄})`);
  43.         return 文本字符串;
  44.     }else{console.log("未找到内存地址!");}
  45. }
复制代码


回复

使用道具 举报

发表于 2026-3-24 17:31:20 | 显示全部楼层
WPS JSA 剪贴板操作代码(优化版)


长文本无损读写剪贴板(突破 255 字符限制),可直接在 WPS 表格 / 文字 / 演示的 JSA 宏中使用。


完整可用代码



  1. // 【工具函数】剪贴板操作 - 支持超长文本、无乱码、稳定运行
  2. // 获取剪贴板文本(支持超长内容)
  3. function GetClipboardText() {
  4.     let text = "";
  5.     const exec = ExecuteExcel4Macro;
  6.    
  7.     if (exec('CALL("user32","OpenClipboard","JJ",0)')) {
  8.         const hMem = exec('CALL("user32","GetClipboardData","JJ",1)');
  9.         if (hMem) {
  10.             const ptr = exec('CALL("kernel32","GlobalLock","JJ",' + hMem + ')');
  11.             const len = exec('CALL("kernel32","lstrlenA","JJ",' + ptr + ')');
  12.             
  13.             let offset = 0, chunk = 127;
  14.             while (offset < len) {
  15.                 const part = exec('CALL("kernel32","lstrcpynW","FFJJ","",' + (ptr + offset) + ',' + chunk + ')');
  16.                 text += part;
  17.                 offset += chunk;
  18.             }
  19.             
  20.             exec('CALL("kernel32","GlobalUnlock","JJ",' + hMem + ')');
  21.         }
  22.         exec('CALL("user32","CloseClipboard","J")');
  23.     }
  24.     return text;
  25. }

  26. // 设置剪贴板文本(支持超长内容)
  27. function SetClipboardText(content) {
  28.     const exec = ExecuteExcel4Macro;
  29.     if (!exec('CALL("user32","OpenClipboard","AJ",0)')) { alert("打开剪贴板失败"); return; }
  30.     exec('CALL("user32","EmptyClipboard","A")');

  31.     // 分段处理(规避255字节限制)
  32.     const chunks = [], maxLen = 127;
  33.     for (let i = 0; i < content.length; i += maxLen) chunks.push(content.slice(i, i + maxLen));
  34.    
  35.     // 计算总字节
  36.     let totalBytes = 0;
  37.     chunks.forEach(s => totalBytes += exec('CALL("kernel32","lstrlenA","JF","' + s + '")'));

  38.     // 分配内存
  39.     const hMem = exec('CALL("kernel32","GlobalAlloc","JJJ",2,' + (totalBytes + 1) + ')');
  40.     if (!hMem) { exec('CALL("user32","CloseClipboard","A")'); return; }

  41.     // 写入内存
  42.     const ptr = exec('CALL("kernel32","GlobalLock","JJ",' + hMem + ')');
  43.     let offset = 0;
  44.     chunks.forEach(s => {
  45.         exec('CALL("kernel32","lstrcpynW","JJFJ",' + (ptr + offset) + ',"' + s + '",' + s.length + ')');
  46.         offset += exec('CALL("kernel32","lstrlenA","JF","' + s + '")');
  47.     });

  48.     exec('CALL("kernel32","GlobalUnlock","JJ",' + hMem + ')');
  49.     exec('CALL("user32","SetClipboardData","JJJ",1,' + hMem + ')');
  50.     exec('CALL("user32","CloseClipboard","A")');
  51. }

  52. // ==================== 测试主函数 ====================
  53. function TestClipboard() {
  54.     // 读取剪贴板原有文本
  55.     var oldText = GetClipboardText();
  56.    
  57.     // 加工文本
  58.     var newText = "===== 处理开始 =====\n" + oldText + "\n===== 处理结束 =====";
  59.    
  60.     // 写入剪贴板
  61.     SetClipboardText(newText);
  62.    
  63.     MsgBox("操作完成!\n已修改剪贴板文本", 0, "成功");
  64. }
复制代码




回复

使用道具 举报

发表于 2026-3-24 17:32:20 | 显示全部楼层
用WPS操作剪切板是什么操作???


二○二六年三月二十四日

点评

有些是表格中内容复制后,需要处理转化然后再粘贴到记事本等。 有些是网页中复制后,经过处理填入表格。以上可能用到剪贴板! 获取/设置剪贴板文本 这两个功能就可以被自定义的按钮调用了。  详情 回复 发表于 2026-3-24 17:52
回复

使用道具 举报

发表于 2026-3-24 17:36:47 | 显示全部楼层
谢谢分享
回复

使用道具 举报

发表于 2026-3-24 17:47:16 | 显示全部楼层
感谢分享!
回复

使用道具 举报

 楼主| 发表于 2026-3-24 17:52:34 | 显示全部楼层
本帖最后由 cutebe 于 2026-3-30 15:33 编辑
邪恶海盗 发表于 2026-3-24 17:32
用WPS操作剪切板是什么操作???

有些是表格中内容复制后,需要处理转化然后再粘贴到记事本等。
有些是网页中复制后,经过处理填入表格。以上可能用到剪贴板!
获取/设置剪贴板文本 这两个功能就可以被自定义的按钮调用了。

===== ===== ===== ===== =====
在WPS的JSA宏中,设置/获取剪贴板文本。分离字符串与内存交换操作函数:
  1. function 设置剪贴板文本(文本内容){    //参数为字符串
  2.     let 执行宏调用=ExecuteExcel4Macro;    //调用系统应用接口。简化书写
  3.     if (执行宏调用(`CALL("User32","OpenClipboard","AJ",0)`)){    //打开剪贴板
  4.         if(执行宏调用(`CALL("User32","EmptyClipboard","A")`)){    //清空剪切板
  5.             const 内存句柄=字符串到内存(文本内容);    //*****调用函数*****/
  6.             if(内存句柄){    //console.log("内存块大小:"+执行宏调用(`CALL("Kernel32","GlobalSize","JJ",${内存句柄})`));
  7.                 let 剪贴板文本=执行宏调用(`CALL("User32","SetClipboardData","JJJ",1,${内存句柄})`);
  8.                 执行宏调用(`CALL("Kernel32","GlobalFree","JJ",${内存句柄})`);    //释放内存
  9.             }else{console.log("未能分配内存!");}    //数据放入剪切板,参数为1(CF_TEXT)则返回文本
  10.         }else{console.log('未能清空剪贴板!');}
  11.         执行宏调用(`CALL("User32","CloseClipboard","A")`);    //关闭剪贴板,以释放控制权。
  12.     }else{alert('无法打开剪贴板!');}
  13. }

  14. function 获取剪贴板文本(){    //返回字符串
  15.     const 执行宏调用=ExecuteExcel4Macro;
  16.     if(执行宏调用(`CALL("User32","OpenClipboard","JJ",0)`)){    //打开剪贴板
  17.         const 内存句柄=执行宏调用(`CALL("User32","GetClipboardData","JJ",1)`);    //获取剪贴板文本,返回句柄。
  18.         if(内存句柄){    //锁定全局内存对象,获取内存地址
  19.             var 剪贴板文本=内存取字符串(内存句柄);    //*****调用函数*****/
  20.         }else{console.log("未能获取剪贴板数据的内存句柄!");}
  21.         执行宏调用(`CALL("User32","CloseClipboard","J")`);    //关闭剪贴板,让其它应用可以使用剪贴板
  22.         return 剪贴板文本;    //返回字符串
  23.     }else{alert('无法打开剪贴板!');}
  24. }

  25. function 字符串到内存(字符串){    //返回内存句柄
  26.     const 执行宏调用=ExecuteExcel4Macro;
  27.     let 分段文本=[],分段字节=[],总字节=0,段长度=127;    //可申请256字节内存,结束符1字节,汉字最长127
  28.     for(let 起始=i=0;起始<字符串.length;起始+=段长度,i++){
  29.         分段文本.push(字符串.slice(起始,起始+段长度));
  30.         分段字节.push(分段文本[i].replace(/[^\x00-\xff]/g,'xx').length);    //一个汉字占两个字节
  31.         总字节+=分段字节[i];
  32.     }    //分配内存    //分配GlobalAlloc对应销毁GlobalFree    //分配LocalAlloc对应销毁LocalFree
  33.     const 内存句柄=执行宏调用(`CALL("Kernel32","GlobalAlloc","JJJ",${0x42},${总字节+1})`);
  34.     if(内存句柄){    //内存分配成功,然后锁写解内存。    //锁定全局内存对象,会返回其内存地址
  35.         const 内存地址=执行宏调用(`CALL("Kernel32","GlobalLock","JJ",${内存句柄})`);
  36.         for(let 偏移字节=i=0;i<分段文本.length;i++){    //字符串(JS变量)分批复制到刚申请的内存中。分批是因为有限长
  37.             执行宏调用(`CALL("Kernel32","lstrcpynW","JJFJ",${内存地址+偏移字节},"${分段文本[i].replace(/"/g,'""')}",${分段字节[i]})`);
  38.             偏移字节+=分段字节[i];    //返回地址J。    //字符串中含双引号出错,因此替换成单引号
  39.         }
  40.         执行宏调用(`CALL("Kernel32","GlobalUnlock","JJ",${内存句柄})`);    //解锁全局内存对象,以释放资源
  41.         return 内存句柄;
  42.     }else{console.log("未能分配内存!");}
  43. }

  44. function 内存取字符串(内存句柄){    //返回字符串
  45.     const 执行宏调用=ExecuteExcel4Macro,段长度=127;
  46.     const 内存地址=执行宏调用(`CALL("Kernel32","GlobalLock","JJ",${内存句柄})`);    //锁定,获取内存地址
  47.     if(内存地址){
  48.         const 总字节=执行宏调用(`CALL("Kernel32","lstrlenA","JJ",${内存地址})`);    //内存文本字节数
  49.         let 偏移字节=0,文本字符串=分段文本='';
  50.         while(偏移字节<总字节){    //返回字符串F。    //返回超255字节时,也需要分段操作
  51.             分段文本=执行宏调用(`CALL("Kernel32","lstrcpynW","FFJJ","",${内存地址+偏移字节},${段长度})`);
  52.             //偏移字节+=执行宏调用(`CALL("Kernel32","lstrlenA","JC","${分段文本.replace(/["\n]/g,'x')}")`);
  53.             偏移字节+=分段文本.replace(/[^\x00-\xff]/g,'xx').length;    //计算字符串字节数
  54.             文本字符串+=分段文本;
  55.         }    //解锁全局内存对象,以释放资源
  56.         执行宏调用(`CALL("Kernel32","GlobalUnlock","JJ",${内存句柄})`);
  57.         return 文本字符串;
  58.     }else{console.log("未找到内存地址!");}
  59. }
复制代码
.replace(/"/g,'""')

回复

使用道具 举报

发表于 2026-3-24 19:43:27 | 显示全部楼层
学习一下
回复

使用道具 举报

发表于 2026-3-24 20:48:12 | 显示全部楼层
宏是很好用
回复

使用道具 举报

发表于 2026-3-25 10:35:03 | 显示全部楼层
学习一下!
回复

使用道具 举报

发表于 2026-3-26 10:16:44 | 显示全部楼层
学到了,感谢楼主
回复

使用道具 举报

发表于 6 天前 | 显示全部楼层
学习了,多谢分享
回复

使用道具 举报

 楼主| 发表于 前天 20:31 | 显示全部楼层
有一个计算字符串字节数的新方法,调用表格函数LENB。

原方法:
  1. 偏移字节+=分段文本.replace(/[^\x00-\xff]/g,'xx').length;    //计算字符串“分段文本”的字节数
复制代码

新方法:
  1. function 测试计算字符中字节数新方法(){
  2.         let 字符串="你好阿小明。\nABC";        //应该是16字节
  3.         let 字节数=ExecuteExcel4Macro(`LENB("${字符串}")`);        //表格JSA中计算字符串字节数
  4.         console.log("字符串的字节数:"+字节数);
  5. }
复制代码

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1|闽公网安备35020302032614号 )

GMT+8, 2026-4-14 20:18

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表