Skip to content

Commit

Permalink
SetClipboardData requires a memory object allocated with `GMEM_MOVE…
Browse files Browse the repository at this point in the history
…ABLE` (and locks) (#8490)

* SetClipboardData requires a memory object allocated with GMEM_MOVEABLE (and locks)

* Use memmove instead of wcscpy

* We need a str, not bytes

* Remove old comment
  • Loading branch information
misl6 committed Dec 2, 2023
1 parent 49b4819 commit ef5d179
Showing 1 changed file with 53 additions and 19 deletions.
72 changes: 53 additions & 19 deletions kivy/core/clipboard/clipboard_winctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,52 @@
c_char_p = ctypes.c_char_p
c_wchar_p = ctypes.c_wchar_p

GlobalLock = kernel32.GlobalLock
GlobalLock.argtypes = [wintypes.HGLOBAL]
GlobalLock.restype = wintypes.LPVOID

GlobalUnlock = kernel32.GlobalUnlock
GlobalUnlock.argtypes = [wintypes.HGLOBAL]
GlobalUnlock.restype = wintypes.BOOL

CF_UNICODETEXT = 13
GMEM_MOVEABLE = 0x0002


class ClipboardWindows(ClipboardBase):

def _copy(self, data):
self._ensure_clipboard()
self.put(data, self._clip_mime_type)

def get(self, mimetype='text/plain'):
GetClipboardData = user32.GetClipboardData
GetClipboardData.argtypes = [wintypes.UINT]
GetClipboardData.restype = wintypes.HANDLE

user32.OpenClipboard(user32.GetActiveWindow())
# Standard Clipboard Format "1" is "CF_TEXT"
pcontents = GetClipboardData(13)

# GetClipboardData returns a HANDLE to the clipboard data
# which is a memory object containing the data
# See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclipboarddata # noqa: E501
pcontents = GetClipboardData(CF_UNICODETEXT)

# if someone pastes a FILE, the content is None for SCF 13
# and the clipboard is locked if not closed properly
if not pcontents:
user32.CloseClipboard()
return ''
data = c_wchar_p(pcontents).value.encode(self._encoding)

# The handle returned by GetClipboardData is a memory object
# and needs to be locked to get the actual pointer to the data
pcontents_locked = GlobalLock(pcontents)
data = c_wchar_p(pcontents_locked).value
GlobalUnlock(pcontents)

user32.CloseClipboard()
return data

def put(self, text, mimetype='text/plain'):

def put(self, text, mimetype="text/plain"):
SetClipboardData = user32.SetClipboardData
SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE]
SetClipboardData.restype = wintypes.HANDLE
Expand All @@ -52,20 +75,31 @@ def put(self, text, mimetype='text/plain'):
user32.OpenClipboard(user32.GetActiveWindow())
user32.EmptyClipboard()

# this allocates memory for the string and returns a handle to it
# allocates fixed memory, len + 2 is for the null character
# no need to call GlobalFree here as SetClipboardData will do for you
# noqa: E501 see: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclipboarddata#parameters
GMEM_FIXED = 0x0000
hCd = GlobalAlloc(GMEM_FIXED, len(text) + 2)

# copy the string into the allocated memory
msvcrt.wcscpy(c_wchar_p(hCd), text)

# standard clipboard format for unicode text
# noqa: E501 see https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats#constants
CF_UNICODETEXT = 13
# set the clipboard data, later used by GetClipboardData()
# The wsclen function returns the number of
# wide characters in a string (not including the null character)
text_len = msvcrt.wcslen(text) + 1

# According to the docs regarding SetClipboardDatam, if the hMem
# parameter identifies a memory object, the object must have
# been allocated using the GMEM_MOVEABLE flag.
# See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclipboarddata # noqa: E501
# The size of the memory object is the number of wide characters in
# the string plus one for the terminating null character
hCd = GlobalAlloc(
GMEM_MOVEABLE, ctypes.sizeof(ctypes.c_wchar) * text_len
)

# Since the memory object is allocated with GMEM_MOVEABLE, should be
# locked to get the actual pointer to the data.
hCd_locked = GlobalLock(hCd)
ctypes.memmove(
c_wchar_p(hCd_locked),
c_wchar_p(text),
ctypes.sizeof(ctypes.c_wchar) * text_len,
)
GlobalUnlock(hCd)

# Finally, set the clipboard data (and then close the clipboard)
SetClipboardData(CF_UNICODETEXT, hCd)
user32.CloseClipboard()

Expand Down

0 comments on commit ef5d179

Please sign in to comment.