11import { clipboard } from 'electron' ;
22import path from 'path' ;
33
4+ // 仅在 Windows 平台辅助操作剪贴板多文件格式。
45type ClipboardExModule = typeof import ( 'electron-clipboard-ex' ) ;
56
67const DROPFILES_HEADER_SIZE = 20 ;
78
89let clipboardExModule : ClipboardExModule | null = null ;
910
11+ /**
12+ * Windows 平台专用:尝试加载第三方库 electron-clipboard-ex。
13+ * 这个库能够调用系统底层接口写入“文件复制”数据,成功率更高。
14+ * 其他系统无需加载它,因此这里做了“按需加载”的处理。
15+ */
1016const ensureClipboardEx = ( ) : ClipboardExModule | null => {
1117 if ( process . platform !== 'win32' ) return null ;
1218 if ( clipboardExModule ) return clipboardExModule ;
@@ -19,9 +25,20 @@ const ensureClipboardEx = (): ClipboardExModule | null => {
1925 return clipboardExModule ;
2026} ;
2127
28+ /**
29+ * 把一组文件路径变成 Windows 规定的文本格式。
30+ * 要求:每个路径之间用单个空字符分隔,最后再额外放两个空字符,表示列表结束。
31+ * Windows 资源管理器会按这个格式解析我们复制到剪贴板的文件。
32+ */
2233const buildWindowsFileListPayload = ( files : string [ ] ) : Buffer =>
2334 Buffer . from ( `${ files . join ( '\0' ) } \0\0` , 'utf16le' ) ;
2435
36+ /**
37+ * 构造 CF_HDROP 专用的二进制数据。
38+ * 这是 Windows 复制文件时的底层格式,前 20 字节是固定的结构头,
39+ * 后面紧跟着具体的文件路径(由 buildWindowsFileListPayload 生成)。
40+ * 只要把这个内容写入剪贴板,任何支持粘贴文件的程序都能理解。
41+ */
2542const buildWindowsFileDropBuffer = ( files : string [ ] ) : Buffer => {
2643 const payload = buildWindowsFileListPayload ( files ) ;
2744 const header = Buffer . alloc ( DROPFILES_HEADER_SIZE ) ;
@@ -41,6 +58,11 @@ const buildWindowsFileDropBuffer = (files: string[]): Buffer => {
4158 return result ;
4259} ;
4360
61+ /**
62+ * 复制/移动/创建快捷方式 等不同操作在 Windows 中对应不同的“意图”值。
63+ * Preferred DropEffect 告诉系统:当前剪贴板数据应该以何种方式处理。
64+ * 我们默认写入“copy”,相当于普通的复制粘贴。
65+ */
4466const buildDropEffectBuffer = ( effect : 'copy' | 'move' | 'link' = 'copy' ) => {
4567 const effectMap = {
4668 copy : 1 ,
@@ -52,6 +74,14 @@ const buildDropEffectBuffer = (effect: 'copy' | 'move' | 'link' = 'copy') => {
5274 return buffer ;
5375} ;
5476
77+ /**
78+ * 直接使用 Electron 内置 API 写入多种剪贴板格式。
79+ * 步骤:
80+ * 1. 写入二进制的 CF_HDROP(含头部与路径列表)
81+ * 2. 写入纯文本形式的 FileNameW(备选格式)
82+ * 3. 写入 Preferred DropEffect(告诉系统“这是复制”)
83+ * 全部成功后,读取一次 CF_HDROP 的长度,确认剪贴板里确实有内容。
84+ */
5585const writeWindowsBuffers = ( files : string [ ] ) : boolean => {
5686 try {
5787 clipboard . writeBuffer ( 'CF_HDROP' , buildWindowsFileDropBuffer ( files ) ) ;
@@ -63,6 +93,11 @@ const writeWindowsBuffers = (files: string[]): boolean => {
6393 }
6494} ;
6595
96+ /**
97+ * 如果项目中安装了 electron-clipboard-ex,我们优先使用它。
98+ * 理由:该库通过原生方式与系统交互,兼容性往往优于 Electron 的 JS 层写入。
99+ * 调用成功后,必要时读回文件列表做一次数量校验,确保复制的文件数量正确。
100+ */
66101const writeWithClipboardEx = ( files : string [ ] ) : boolean => {
67102 const clipboardEx = ensureClipboardEx ( ) ;
68103 if ( ! clipboardEx ) return false ;
@@ -78,6 +113,13 @@ const writeWithClipboardEx = (files: string[]): boolean => {
78113 }
79114} ;
80115
116+ /**
117+ * 对外暴露的唯一入口。
118+ * 1. 先把所有路径换成 Windows 可识别的标准形式(path.normalize)。
119+ * 2. 尝试使用 electron-clipboard-ex 写入,如果成功就结束。
120+ * 3. 若第三方库不可用或失败,再退回 Electron 原生写入流程。
121+ * 这一层屏蔽了所有细节,外部调用者只需传入字符串数组即可。
122+ */
81123export const copyFilesToWindowsClipboard = ( files : string [ ] ) : boolean => {
82124 const normalizedFiles = files
83125 . map ( ( filePath ) => path . normalize ( filePath ) )
0 commit comments