// Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build windows package clipboard import ( "syscall" "time" "unsafe" ) const ( cfUnicodetext = 13 gmemMoveable = 0x0002 ) var ( user32 = syscall.MustLoadDLL("user32") openClipboard = user32.MustFindProc("OpenClipboard") closeClipboard = user32.MustFindProc("CloseClipboard") emptyClipboard = user32.MustFindProc("EmptyClipboard") getClipboardData = user32.MustFindProc("GetClipboardData") setClipboardData = user32.MustFindProc("SetClipboardData") kernel32 = syscall.NewLazyDLL("kernel32") globalAlloc = kernel32.NewProc("GlobalAlloc") globalFree = kernel32.NewProc("GlobalFree") globalLock = kernel32.NewProc("GlobalLock") globalUnlock = kernel32.NewProc("GlobalUnlock") lstrcpy = kernel32.NewProc("lstrcpyW") ) // waitOpenClipboard opens the clipboard, waiting for up to a second to do so. func waitOpenClipboard() error { started := time.Now() limit := started.Add(time.Second) var r uintptr var err error for time.Now().Before(limit) { r, _, err = openClipboard.Call(0) if r != 0 { return nil } time.Sleep(time.Millisecond) } return err } func readAll() (string, error) { err := waitOpenClipboard() if err != nil { return "", err } defer closeClipboard.Call() h, _, err := getClipboardData.Call(cfUnicodetext) if h == 0 { return "", err } l, _, err := globalLock.Call(h) if l == 0 { return "", err } text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) r, _, err := globalUnlock.Call(h) if r == 0 { return "", err } return text, nil } func writeAll(text string) error { err := waitOpenClipboard() if err != nil { return err } defer closeClipboard.Call() r, _, err := emptyClipboard.Call(0) if r == 0 { return err } data := syscall.StringToUTF16(text) // "If the hMem parameter identifies a memory object, the object must have // been allocated using the function with the GMEM_MOVEABLE flag." h, _, err := globalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0])))) if h == 0 { return err } defer func() { if h != 0 { globalFree.Call(h) } }() l, _, err := globalLock.Call(h) if l == 0 { return err } r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) if r == 0 { return err } r, _, err = globalUnlock.Call(h) if r == 0 { if err.(syscall.Errno) != 0 { return err } } r, _, err = setClipboardData.Call(cfUnicodetext, h) if r == 0 { return err } h = 0 // suppress deferred cleanup return nil }