Here is a ctypes-only example to set any Python str-type text on the clipboard and read it back. Note that text should not contain nulls as the CF_UNICODETEXT type expects to be null-terminated text.
import ctypes as ct
import ctypes.wintypes as w
CF_UNICODETEXT = 13
NO_ERROR = 0
SIZE_T = ct.c_size_t
GMEM_MOVEABLE = 0x0002
# Error handlers to raise exceptions on failure.
def boolcheck(result, func, args):
if not result:
raise ct.WinError(ct.get_last_error())
def nullcheck(result, func, args):
if result is None:
raise ct.WinError(ct.get_last_error())
return result
def zeroerrorcheck(result, func, args):
if not result:
err = ct.get_last_error()
if err != NO_ERROR:
raise ct.WinError(err)
return result
# Capture GetLastError() code after each call.
# Fully specify argtypes and restype for ctypes type-checking.
kernel32 = ct.WinDLL('kernel32', use_last_error=True)
GlobalLock = kernel32.GlobalLock
GlobalLock.argtypes = w.HGLOBAL,
GlobalLock.restype = w.LPVOID
GlobalLock.errcheck = nullcheck
GlobalAlloc = kernel32.GlobalAlloc
GlobalAlloc.argtypes = w.UINT, SIZE_T
GlobalAlloc.restype = w.HGLOBAL
GlobalAlloc.errcheck = nullcheck
GlobalUnlock = kernel32.GlobalUnlock
GlobalUnlock.argtypes = w.HGLOBAL,
GlobalUnlock.restype = w.BOOL
GlobalUnlock.errcheck = zeroerrorcheck
user32 = ct.WinDLL('user32', use_last_error=True)
OpenClipboard = user32.OpenClipboard
OpenClipboard.argtypes = w.HWND,
OpenClipboard.restype = w.BOOL
OpenClipboard.errcheck = boolcheck
GetClipboardData = user32.GetClipboardData
GetClipboardData.argtypes = w.UINT,
GetClipboardData.restype = w.HANDLE
GetClipboardData.errcheck = nullcheck
SetClipboardData = user32.SetClipboardData
SetClipboardData.argtypes = w.UINT, w.HANDLE
SetClipboardData.restype = w.HANDLE
SetClipboardData.errcheck = nullcheck
CloseClipboard = user32.CloseClipboard
CloseClipboard.argtypes = ()
CloseClipboard.restype = w.BOOL
CloseClipboard.errcheck = boolcheck
EmptyClipboard = user32.EmptyClipboard
EmptyClipboard.argtypes = ()
EmptyClipboard.restype = w.BOOL
EmptyClipboard.errcheck = boolcheck
GetForegroundWindow = user32.GetForegroundWindow
GetForegroundWindow.argtypes = ()
GetForegroundWindow.restype = w.HWND
def get_clipboard_text():
OpenClipboard(GetForegroundWindow())
hmem = GetClipboardData(CF_UNICODETEXT)
pmem = GlobalLock(hmem)
text = ct.wstring_at(pmem)
GlobalUnlock(hmem)
CloseClipboard()
return text
def set_clipboard_text(text):
ztext = text + '\x00' # null terminator required
OpenClipboard(None)
EmptyClipboard()
hmem = GlobalAlloc(GMEM_MOVEABLE, len(ztext) * ct.sizeof(w.WCHAR))
pmem = GlobalLock(hmem)
btext = ztext.encode('utf-16le')
ct.memmove(pmem, btext, len(btext))
GlobalUnlock(hmem)
SetClipboardData(CF_UNICODETEXT, hmem)
CloseClipboard()
set_clipboard_text('马克')
print(get_clipboard_text())
Per OpenClipboard documentation remarks:
If an application calls OpenClipboard with hwnd set to NULL, EmptyClipboard sets the clipboard owner to NULL; this causes SetClipboardData to fail.
My experience is that using NULL (None in Python) with OpenClipboard does not cause SetClipboardData to fail, but doesn't prevent other apps using the clipboard while open (potential race condition?). When a valid Window handle is used, other apps will fail to open the clipboard until CloseClipboard is called, so some retrying and error checking will be needed. Probably best to use a valid owner. I've used GetForegroundWindow above.