Discussion:
win32clipboard writing to clipboard on Windows 11
(too old to reply)
Rob Cliffe
2024-06-17 19:27:51 UTC
Permalink
Recently I acquired a new laptop running WIndows 11; my previous one
uses WIndows 10.  I encountered a strange problem:
I am using the win32clipboard backage (part of pywin32), and when I use
SetClipboardData() to write text which consists ***entirely of digits***
to the clipboard, I either get an error (not always the same error
message) or a program crash.  The problem does not appear if I use
SetClipboardText() instead.  The problem does not occur on my old
machine (where I used the feature extensively).

Sample program:

from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
SetClipboardData(CF_UNICODETEXT, "A0")
SetClipboardData(CF_UNICODETEXT, "0A")
SetClipboardText("0", CF_UNICODETEXT)
print("OK so far")
SetClipboardData(CF_UNICODETEXT, "0")
CloseClipboard()

Sample output:

OK so far
Traceback (most recent call last):
  File "C:\TEST*.PY", line 8, in <module>
    SetClipboardData(CF_UNICODETEXT, "0")
pywintypes.error: (0, 'SetClipboardData', 'No error message is available')

Can anyone shed light on this?
Best wishes
Rob Cliffe
MRAB
2024-06-18 01:30:46 UTC
Permalink
Post by Rob Cliffe
Recently I acquired a new laptop running WIndows 11; my previous one
I am using the win32clipboard backage (part of pywin32), and when I use
SetClipboardData() to write text which consists ***entirely of digits***
to the clipboard, I either get an error (not always the same error
message) or a program crash.  The problem does not appear if I use
SetClipboardText() instead.  The problem does not occur on my old
machine (where I used the feature extensively).
from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
SetClipboardData(CF_UNICODETEXT, "A0")
SetClipboardData(CF_UNICODETEXT, "0A")
SetClipboardText("0", CF_UNICODETEXT)
print("OK so far")
SetClipboardData(CF_UNICODETEXT, "0")
CloseClipboard()
OK so far
  File "C:\TEST*.PY", line 8, in <module>
    SetClipboardData(CF_UNICODETEXT, "0")
pywintypes.error: (0, 'SetClipboardData', 'No error message is available')
Can anyone shed light on this?
Best wishes
Rob Cliffe
Post by Rob Cliffe
from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
1830508101640
Post by Rob Cliffe
Post by Rob Cliffe
CloseClipboard()
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "0")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.error: (6, 'SetClipboardData', 'The handle is invalid.')
Post by Rob Cliffe
Post by Rob Cliffe
CloseClipboard()
It looks like it's something to memory ownership:

https://stackoverflow.com/questions/1264137/how-to-copy-string-to-clipboard-in-c

If you're putting text on the clipboard, why not just use
SetClipboardText()? That's what I do.
Thomas Passin
2024-06-18 02:23:07 UTC
Permalink
Post by MRAB
Post by Rob Cliffe
Recently I acquired a new laptop running WIndows 11; my previous one
I am using the win32clipboard backage (part of pywin32), and when I use
SetClipboardData() to write text which consists ***entirely of digits***
to the clipboard, I either get an error (not always the same error
message) or a program crash.  The problem does not appear if I use
SetClipboardText() instead.  The problem does not occur on my old
machine (where I used the feature extensively).
from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
SetClipboardData(CF_UNICODETEXT, "A0")
SetClipboardData(CF_UNICODETEXT, "0A")
SetClipboardText("0", CF_UNICODETEXT)
print("OK so far")
SetClipboardData(CF_UNICODETEXT, "0")
CloseClipboard()
OK so far
    File "C:\TEST*.PY", line 8, in <module>
      SetClipboardData(CF_UNICODETEXT, "0")
pywintypes.error: (0, 'SetClipboardData', 'No error message is
available')
Can anyone shed light on this?
Best wishes
Rob Cliffe
Post by Rob Cliffe
from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
1830508101640
Post by Rob Cliffe
Post by Rob Cliffe
CloseClipboard()
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "0")
  File "<stdin>", line 1, in <module>
pywintypes.error: (6, 'SetClipboardData', 'The handle is invalid.')
Post by Rob Cliffe
Post by Rob Cliffe
CloseClipboard()
https://stackoverflow.com/questions/1264137/how-to-copy-string-to-clipboard-in-c
If you're putting text on the clipboard, why not just use
SetClipboardText()? That's what I do.
If you can make a change, and you only need to work with text on the
clipboard, you could change to use pyperclip. It also works on Linux,
if you might care about that in the future. It's available as a pip
install. It's easier to use than the win32 approach.
Eryk Sun
2024-06-18 07:19:09 UTC
Permalink
On Mon, Jun 17, 2024 at 8:36 PM MRAB via Python-list
Post by Rob Cliffe
SetClipboardData(CF_UNICODETEXT, "0")
CloseClipboard()
win32clipboard.SetClipboardData() first tries to covert the second
argument as an integer handle to global memory, which gets passed to
WinAPI SetClipboardData(). The integer conversion is basically via
int(). With int("0"), it's passing a NULL handle value, which
instructs the window manager to query the data from the window that
was associated via OpenClipboard(), if any. Since no memory handle is
passed in this case, SetClipboardData() returns NULL.
win32clipboard.SetClipboardData() misinterprets this as failure and
raises an exception for whatever random error code is currently set in
the thread's last error value. On the other hand, for a numeric text
string with a nonzero value, such as "123",
win32clipboard.SetClipboardData() will raise an exception for the
error code ERROR_INVALID_HANDLE (6), unless the integer value happens
to be a valid global memory handle value.

I recommend just using win32clipboard.SetClipboardText(). Otherwise I
don't see an easy workaround given the peculiar design of
win32clipboard.SetClipboardData(). You'd have to manually allocate a
block of global memory, copy the numeric text string into it, and pass
the global memory handle to win32clipboard.SetClipboardData(). For
example:

import ctypes
import win32clipboard
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

GMEM_MOVEABLE = 0x0002

kernel32.GlobalAlloc.restype = wintypes.HGLOBAL
kernel32.GlobalFree.argtypes = (wintypes.HGLOBAL,)
kernel32.GlobalLock.restype = wintypes.LPVOID
kernel32.GlobalLock.argtypes = (wintypes.HGLOBAL,)
kernel32.GlobalUnlock.argtypes = (wintypes.HGLOBAL,)

def global_alloc_text(text):
array_t = ctypes.c_wchar * (len(text) + 1)
hMem = kernel32.GlobalAlloc(GMEM_MOVEABLE, ctypes.sizeof(array_t))
if not hMem:
raise ctypes.WinError(ctypes.get_last_error())
pMem = kernel32.GlobalLock(hMem)
try:
try:
array_t.from_address(pMem).value = text
finally:
kernel32.GlobalUnlock(hMem)
except:
kernel32.GlobalFree(hMem)
raise
return hMem

def set_clipboard_text(text):
hMem = global_alloc_text(text)
try:
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,
hMem)
# Now the system owns the global memory.
except:
kernel32.GlobalFree(hMem)
Eryk Sun
2024-06-18 07:26:39 UTC
Permalink
Post by Eryk Sun
hMem = global_alloc_text(text)
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,
hMem)
# Now the system owns the global memory.
kernel32.GlobalFree(hMem)
Oops, that suppresses the exception. Fixed:

def set_clipboard_text(text):
hMem = global_alloc_from_text(text)
try:
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,
hMem)
# Now the system owns the global memory.
except:
kernel32.GlobalFree(hMem)
raise

Loading...