我试图使用带有user32.dll的系统调用来获取剪贴板的内容。我希望它是来自打印屏幕的图像数据。
现在我得到了这个:
if opened := openClipboard(0); !opened {
fmt.Println("Failed to open Clipboard")
}
handle := getClipboardData(CF_BITMAP)
// get buffer
img, _, err := Decode(buffer)
我需要使用句柄将数据放入可读缓冲区。
我从AllenDang / w32和github上的atotto / clipboard获得了一些灵感。基于atotto的实现,以下内容适用于文本:
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(handle))[:])
但是如何获得包含我可以解码的图像数据的缓冲区?
[更新]
通过@kostix提供的解决方案,我将一个半工作的例子拼凑在一起:
image.RegisterFormat("bmp", "bmp", bmp.Decode, bmp.DecodeConfig)
if opened := w32.OpenClipboard(0); opened == false {
fmt.Println("Error: Failed to open Clipboard")
}
//fmt.Printf("Format: %d\n", w32.EnumClipboardFormats(w32.CF_BITMAP))
handle := w32.GetClipboardData(w32.CF_DIB)
size := globalSize(w32.HGLOBAL(handle))
if handle != 0 {
pData := w32.GlobalLock(w32.HGLOBAL(handle))
if pData != nil {
data := (*[1 << 25]byte)(pData)[:size]
// The data is either in DIB format and missing the BITMAPFILEHEADER
// or there are other issues since it can't be decoded at this point
buffer := bytes.NewBuffer(data)
img, _, err := image.Decode(buffer)
if err != nil {
fmt.Printf("Failed decoding: %s", err)
os.Exit(1)
}
fmt.Println(img.At(0, 0).RGBA())
}
w32.GlobalUnlock(w32.HGLOBAL(pData))
}
w32.CloseClipboard()
AllenDang / w32包含了您需要的大部分内容,但有时您需要自己实现一些内容,例如globalSize():
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procGlobalSize = modkernel32.NewProc("GlobalSize")
)
func globalSize(hMem w32.HGLOBAL) uint {
ret, _, _ := procGlobalSize.Call(uintptr(hMem))
if ret == 0 {
panic("GlobalSize failed")
}
return uint(ret)
}
也许有人会想出一个获得BMP数据的解决方案。与此同时,我将走另一条路。
答案 0 :(得分:1)
@JimB是正确的:user32!GetClipboardData()
返回HGLOBAL
,评论示例over there建议使用kernel32!GlobalLock()
a)全局锁定该句柄,b)产生一个正确指向它所引用的内存。
完成后,您需要kernel32!GlobalUnlock()
手柄。
至于将从Win32 API函数获得的指针转换为Go可读的东西,通常的技巧是将指针转换为一个非常大的切片。引用"the Go wiki article on cgo
"的“将C数组转换为Go切片”:
创建由C数组支持的Go切片(不复制原始数据) 数据),需要在运行时获取此长度并使用类型 转换为指向非常大的数组的指针,然后将其切片为 您想要的长度(例如,如果您使用的是Go&gt;或更高版本,请记住设置上限)(请参阅http://play.golang.org/p/XuC0xqtAIC了解 可运行的例子):
import "C" import "unsafe" ... var theCArray *C.YourType = C.getTheArray() length := C.getTheArrayLength() slice := (*[1 << 30]C.YourType)(unsafe.Pointer(theCArray))[:length:length]
重要的是要记住Go垃圾收集器不会 与此数据交互,如果它从C侧释放 事情,使用切片的任何Go代码的行为是不确定的。
在你的情况下,它会更简单:
h := GlobalLock()
defer GlobalUnlock(h)
length := somehowGetLengthOfImageInTheClipboard()
slice := (*[1 << 30]byte)(unsafe.Pointer((uintptr(h)))[:length:length]
然后你需要实际读取位图。
这取决于可从剪贴板导出的设备无关位图(DIB)的格式。
通常,BITMAPINFOHEADER
等的定义可以在MSDN网站上在线轻松获取。