当执行复制操作后,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
