当执行复制操作后,Windows 会同时保存多种格式的数据以保证各个软件能取到自己认识的格式。
为什么会有自定义剪贴板格式
标准格式的局限
虽然 windows 系统内置了一些常见的剪贴板格式(如文本、图片、文件等),但这些格式无法涵盖所有应用的需求。例如:
- Word 可能需要传递带格式的文本(如 RTF、HTML)。
- Photoshop 可能需要传递带有图层信息的图片数据。
- Excel 可能需要传递带有公式和单元格格式的数据。
自定义格式的作用
当应用程序需要在剪贴板中存储特殊类型的数据,而这些数据没有对应的标准格式时,就会注册一个自定义格式(格式ID大于0xC000),并用这个格式来存储和读取数据。
应用程序通过 RegisterClipboardFormat 注册一个唯一的格式名,系统返回一个格式ID。之后应用可以用这个ID在剪贴板中存取数据。其他支持该格式的应用也可以识别和读取这些数据。
如果目标应用不认识这个自定义格式,就会忽略它,转而使用标准格式(如文本、图片等)。这样可以保证最大兼容性,同时又能支持高级功能。
举例:
Word 复制内容时,剪贴板里可能同时有:纯文本(CF_TEXT)、富文本(RTF)、HTML、Word自定义格式(如“MSWordDoc”)。
Photoshop 复制图片时,可能有:位图(CF_BITMAP)、自定义格式(如“Photoshop Image”)。
代码
#Requires AutoHotkey v2.0 #SingleInstance force ; 创建GUI窗口 myGui := Gui() myGui.Title := "剪贴板内容格式检测" myGui.SetFont("s10") ; 添加控件 myGui.Add("Text", , "复制内容后点击下方按钮:") myGui.Add("Button", "w200 h30", "检测剪贴板数据类型").OnEvent("Click", DetectClipboard) myGui.Add("Edit", "w500 h240 vResultText ReadOnly Multi WantReturn VScroll", "点击按钮开始检测...") ; 显示GUI myGui.Show() ; 标准格式 stdFormats := Map( 1, "CF_TEXT", ; ANSI 编码的文本(最常见的文本格式,兼容性最好) 2, "CF_BITMAP", ; 图片内容(不是图片文件,是"二进制"图片内容) 3, "CF_METAFILEPICT", 4, "CF_SYLK", 5, "CF_DIF", 6, "CF_TIFF", 7, "CF_OEMTEXT", ; OEM 编码的文本,主要用于老式 DOS 程序或控制台程序,使用 OEM 代码页(如 437、936 等,DOS 下常用编码) 8, "CF_DIB", ; 图片内容(可以有透明度,但不一定会存入) 9, "CF_PALETTE", 10, "CF_PENDATA", 11, "CF_RIFF", 12, "CF_WAVE", 13, "CF_UNICODETEXT", ; Unicode(UTF-16LE)编码的文本,支持所有语言和字符 14, "CF_ENHMETAFILE", 15, "CF_HDROP", ; 文件(在资源管理器中复制文件) 16, "CF_LOCALE", ; 文本数据的语言/区域(如中文、英文、日文等),通常与 CF_TEXT 或 CF_UNICODETEXT 一起出现,帮助目标程序正确解释文本内容 17, "CF_DIBV5" ; 图片内容(含透明度和色彩信息的 CF_BITMAP) ) ; 检测剪贴板内容的函数 DetectClipboard(*) { try { if !DllCall("OpenClipboard", "ptr", 0) return "无法打开剪贴板" formats := [] fmt := DllCall("EnumClipboardFormats", "uint", 0) while (fmt) { name := "" buf := Buffer(256, 0) if (fmt >= 0xC000) { ; 自定义格式 if DllCall("GetClipboardFormatName", "uint", fmt, "ptr", buf, "int", 256) name := " OtherFormat: " StrGet(buf) else name := "Unknown" } else { ; 标准格式 name := "StandardFormat: " stdFormats[fmt] } value := GetClipboardText(fmt) name := value ? name . " [" . value . "]" : name formats.Push(name) fmt := DllCall("EnumClipboardFormats", "uint", fmt) } ; 关闭剪贴板 DllCall("CloseClipboard") result := Join(formats) } catch Error as e { result := "检测失败: " . e.Message } ; 更新显示结果 myGui["ResultText"].Value := "检测结果: `n" . result } GetClipboardText(typeCode) { text := "" if hMem := DllCall("GetClipboardData", "uint", typeCode, "ptr") { if (typeCode = 15) { ; CF_HDROP fileCount := DllCall("shell32\DragQueryFileW", "ptr", hMem, "uint", 0xFFFFFFFF, "ptr", 0, "uint", 0) files := [] loop fileCount { len := DllCall("shell32\DragQueryFileW", "ptr", hMem, "uint", A_Index - 1, "ptr", 0, "uint", 0) buf := Buffer((len + 1) * 2) DllCall("shell32\DragQueryFileW", "ptr", hMem, "uint", A_Index - 1, "ptr", buf, "uint", len + 1) files.Push(StrGet(buf, "UTF-16")) } text := Join(files, ",") } else if (typeCode = 13) { ; CF_UNICODETEXT if pMem := DllCall("GlobalLock", "ptr", hMem, "ptr") { ; 读取Unicode字符串 text := StrGet(pMem, "UTF-16") DllCall("GlobalUnlock", "ptr", hMem) } } else { } } return text } Join(arr, separator := "`n") { result := "" for v in arr result .= v . separator return RTrim(result, separator) } ; 保持脚本运行 return