//go:build windows package clipboard import ( "fmt" "syscall" "unsafe" ) const cfUnicodeText = 13 var ( user32 = syscall.NewLazyDLL("user32.dll") kernel32 = syscall.NewLazyDLL("kernel32.dll") procOpenClipboard = user32.NewProc("OpenClipboard") procCloseClipboard = user32.NewProc("CloseClipboard") procEmptyClipboard = user32.NewProc("EmptyClipboard") procGetClipboardData = user32.NewProc("GetClipboardData") procSetClipboardData = user32.NewProc("SetClipboardData") procGlobalAlloc = kernel32.NewProc("GlobalAlloc") procGlobalLock = kernel32.NewProc("GlobalLock") procGlobalUnlock = kernel32.NewProc("GlobalUnlock") procGlobalFree = kernel32.NewProc("GlobalFree") ) type windowsClipboard struct{} // New creates a Windows clipboard using Win32 API (syscall, no CGo). func New() (Clipboard, error) { return newWindowsClipboard() } func newWindowsClipboard() (*windowsClipboard, error) { return &windowsClipboard{}, nil } func (c *windowsClipboard) Read() (string, error) { r1, _, err := procOpenClipboard.Call(0) if r1 == 0 { return "", fmt.Errorf("clipboard read: OpenClipboard: %w", err) } defer procCloseClipboard.Call() h, _, err := procGetClipboardData.Call(uintptr(cfUnicodeText)) if h == 0 { // No text in clipboard — not an error, just empty. return "", nil } p, _, err := procGlobalLock.Call(h) if p == 0 { return "", fmt.Errorf("clipboard read: GlobalLock: %w", err) } defer procGlobalUnlock.Call(h) return syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(p))[:]), nil } func (c *windowsClipboard) Write(text string) error { r1, _, err := procOpenClipboard.Call(0) if r1 == 0 { return fmt.Errorf("clipboard write: OpenClipboard: %w", err) } defer procCloseClipboard.Call() r1, _, err = procEmptyClipboard.Call() if r1 == 0 { return fmt.Errorf("clipboard write: EmptyClipboard: %w", err) } utf16, err := syscall.UTF16FromString(text) if err != nil { return fmt.Errorf("clipboard write: UTF16FromString: %w", err) } // Allocate global memory: size in bytes (2 bytes per UTF-16 unit, including null terminator) byteSize := uintptr(len(utf16) * 2) h, _, err := procGlobalAlloc.Call(0, byteSize) if h == 0 { return fmt.Errorf("clipboard write: GlobalAlloc: %w", err) } p, _, err := procGlobalLock.Call(h) if p == 0 { procGlobalFree.Call(h) return fmt.Errorf("clipboard write: GlobalLock: %w", err) } // Copy UTF-16 data into the locked memory. dst := unsafe.Slice((*uint16)(unsafe.Pointer(p)), len(utf16)) copy(dst, utf16) procGlobalUnlock.Call(h) r1, _, err = procSetClipboardData.Call(uintptr(cfUnicodeText), h) if r1 == 0 { procGlobalFree.Call(h) return fmt.Errorf("clipboard write: SetClipboardData: %w", err) } // Windows now owns the handle — do not free it. return nil }