golang访问剪切板的通常做法是使用"github.com/atotto/clipboard"库,但是这个库只支持读取文本信息,不支持读取图片信息。
这里实现了这个功能,代码地址如下:
github地址为 https://github.com/liushiyuan/goPaste
使用的win32 api如下
const (
cfBITMAP = 2
cfUnicodetext = 13
gmemMoveable = 0x0002
)
var (
user32 = syscall.MustLoadDLL("user32")
isClipboardFormatAvailable = user32.MustFindProc("IsClipboardFormatAvailable")
openClipboard = user32.MustFindProc("OpenClipboard")
closeClipboard = user32.MustFindProc("CloseClipboard")
emptyClipboard = user32.MustFindProc("EmptyClipboard")
getClipboardData = user32.MustFindProc("GetClipboardData")
setClipboardData = user32.MustFindProc("SetClipboardData")
getDC = user32.MustFindProc("GetDC")
kernel32 = syscall.NewLazyDLL("kernel32")
globalAlloc = kernel32.NewProc("GlobalAlloc")
globalFree = kernel32.NewProc("GlobalFree")
globalLock = kernel32.NewProc("GlobalLock")
globalUnlock = kernel32.NewProc("GlobalUnlock")
lstrcpy = kernel32.NewProc("lstrcpyW")
libgdi32 = syscall.NewLazyDLL("gdi32.dll")
createCompatibleDC = libgdi32.NewProc("CreateCompatibleDC")
getObject = libgdi32.NewProc("GetObjectW")
selectObject = libgdi32.NewProc("SelectObject")
getDIBits = libgdi32.NewProc("GetDIBits")
)
type BITMAPINFOHEADER struct {
BiSize uint32
BiWidth int32
BiHeight int32
BiPlanes uint16
BiBitCount uint16
BiCompression uint32
BiSizeImage uint32
BiXPelsPerMeter int32
BiYPelsPerMeter int32
BiClrUsed uint32
BiClrImportant uint32
}
读取图片信息的主要流程如下
首先用isClipboardFormatAvailable 判断剪切板中的信息类型是否为图片,打开剪切板。
func PasteImg(outputPng bool) ([]byte, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if formatAvailable, _, err := isClipboardFormatAvailable.Call(cfBITMAP); formatAvailable == 0 {
return nil, err
}
err := waitOpenClipboard()
if err != nil {
return nil, err
}
获取剪切板资源句柄,再通过句柄获取资源对象,从对象中提取图片的长度、宽度、位数,三个信息。
h, _, err := getClipboardData.Call(cfBITMAP)
if h == 0 {
_, _, _ = closeClipboard.Call()
return nil, err
}
hdc, _, err := getDC.Call(0)
hdcmem, _, err := createCompatibleDC.Call(hdc)
selectObject.Call(hdcmem, h)
bm := make([]byte, 28)
var size uint = 28
getObject.Call(h, uintptr(unsafe.Pointer(&size)), uintptr(unsafe.Pointer(&bm[0])))
width := binary.LittleEndian.Uint32(bm[4:8])
height := binary.LittleEndian.Uint32(bm[8:12])
bmBitsPixel := bm[18]
构造bmp信息头,通过getDIBits获取图片内容,a是bmp信息头,b是保存图片数据的缓冲区
a := new(BITMAPINFOHEADER)
a.BiSize = 40
a.BiWidth = int32(width)
a.BiHeight = int32(height)
a.BiPlanes = 1
a.BiBitCount = uint16(bmBitsPixel)
a.BiSizeImage = (width*uint32(bmBitsPixel) + 31) / 32 * 4 * height
b := make([]byte, a.BiSizeImage)
getDIBits.Call(hdc, h, 0, uintptr(height), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(a)), 0)
按照bmp文件格式,构造数据
w := bytes.NewBuffer([]byte{})
w.Write([]byte{0x42, 0x4d})
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.LittleEndian, a.BiSizeImage+54)
w.Write(bytesBuffer.Bytes())
temp := make([]byte, 8)
temp[4] = 0x36
w.Write(temp)
w.Write([]byte{0x28, 0x00, 0x00, 0x00})
bytesBuffer.Reset()
binary.Write(bytesBuffer, binary.LittleEndian, a.BiWidth)
binary.Write(bytesBuffer, binary.LittleEndian, a.BiHeight)
binary.Write(bytesBuffer, binary.LittleEndian, a.BiPlanes)
binary.Write(bytesBuffer, binary.LittleEndian, a.BiBitCount)
var temp1 uint32
temp1 = 0
binary.Write(bytesBuffer, binary.LittleEndian, temp1)
binary.Write(bytesBuffer, binary.LittleEndian, temp1)
w.Write(bytesBuffer.Bytes())
bytesBuffer.Reset()
temp = make([]byte, 16)
w.Write(temp)
w.Write(b)
基本上使用png图片格式居多,这个可以根据需要将bmp转为png
func bmp2png(w *bytes.Buffer) ([]byte, error) {
var err error
var src image.Image
src, err = bmp.Decode(w)
if err != nil {
return nil, err
}
out := bytes.NewBuffer([]byte{})
png.Encode(out, src)
return out.Bytes(), nil
}