【问题标题】:How can I retrieve an image data buffer from clipboard memory (uintptr)?如何从剪贴板内存 (uintptr) 中检索图像数据缓冲区?
【发布时间】:2016-12-19 10:31:34
【问题描述】:

我正在尝试使用带有 user32.dll 的系统调用来获取剪贴板的内容。我希望它是来自打印屏幕的图像数据。

现在我有这个:

if opened := openClipboard(0); !opened {
    fmt.Println("Failed to open Clipboard")
}

handle := getClipboardData(CF_BITMAP)

// get buffer

img, _, err := Decode(buffer)

我需要使用句柄将数据放入可读缓冲区。

我从 github 上的 AllenDang/w32 和 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 数据。与此同时,我将采取不同的路线。

【问题讨论】:

  • 您需要知道CF_BITMAP 数据有多大。 getClipboardData 是做什么的?
  • 嗯,CF_BITMAP只是告诉调用我们请求的是什么类型的数据,它等于2。图像/位图的大小将与分辨率有关,但我不能来如果这就是您的意思,请提供一个数字。 getClipboardData 返回一个 uintptr,它应该是指向图像数据的指针。我只是不知道访问它的正确方法。我不太明白文本字符串版本是如何工作的,也许您或这里的其他人知道它是如何工作的以及缓冲区版本可能是什么样子。
  • 快速浏览一下github.com/atotto/clipboard,您的示例不适适用于文本,因为handle 不是指向空终止字符串的指针,它是一个WINAPI处理。位图也是如此,指针与句柄不同。这不仅仅是通过指针“检索”一些内存,您应该使用 Windows API 通过给定的句柄访问位图。

标签: windows memory go bmp dib


【解决方案1】:

@JimB 是正确的:user32!GetClipboardData() 返回一个HGLOBAL,并且注释示例over there 建议使用kernel32!GlobalLock() 来a)全局锁定该句柄,并且b)产生一个正确的指向内存的指针它。

完成后,您需要kernel32!GlobalUnlock() 句柄。

关于将从 Win32 API 函数获得的指针转换为 Go 可读的东西,通常的技巧是将指针转换为非常大的切片。引用"the Go wiki article on cgo"的“Turning C arrays into Go slices”:

创建一个由 C 数组支持的 Go 切片(不复制原始 数据),需要在运行时获取此长度并使用类型 转换为指向一个非常大的数组的指针,然后将其切片到 您想要的长度(如果您使用 Go 1.2 > 或更高版本,请记住设置上限),例如(请参阅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) 的格式。

请参阅thisthis 了解一下。

像往常一样,BITMAPINFOHEADER 等的定义很容易在 MSDN 网站上在线获得。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    相关资源
    最近更新 更多