golang 从windows 剪切板 (剪贴板)中读取bmp图片数据的方法

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
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值