pythonwinapictypespython-imaging-library

Copy PIL/PILLOW Image to Windows Clipboard


I've seen this question and i followed every step, changing the code to satisfy my requirements, that are Python3, Pillow, and ctypes. The less libraries, the better.

import ctypes
from PIL import ImageGrab, Image
from io import BytesIO

user32 = ctypes.windll.user32

img = ImageGrab.grab()
output = BytesIO()
img.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()

user32.OpenClipboard()
user32.EmptyClipboard()
user32.SetClipboardData(user32.CF_DIB, data)
user32.CloseClipboard()

That is the stripped code from my script that, i think, is the same code in the question ported to my requirements. When executed, it should copy the current desktop to the clipboard. I get this instead:

File "C:\Users\Gcq\Documents\python\Screen\Screen.py", line 132, in shot
    user32.OpenClipboard()
ValueError: Procedure probably called with not enough arguments (4 bytes missing)

I'm sorry i'm asking such a (probably) easy question here, but i really don't know what is failing, and ctypes is not my thing.


Solution

  • Whew. Apparently the win32clipboard library does simplify some things when compared to ctypes. Your attempt to simply replace one with the other is far from correct.

    So I booted up my Windows virtual machine, installed Pillow and rewrote your program, learning from two other answers:

    import io
    
    import ctypes
    msvcrt = ctypes.cdll.msvcrt
    kernel32 = ctypes.windll.kernel32
    user32 = ctypes.windll.user32
    
    from PIL import ImageGrab
    
    img = ImageGrab.grab()
    output = io.BytesIO()
    img.convert('RGB').save(output, 'BMP')
    data = output.getvalue()[14:]
    output.close()
    
    CF_DIB = 8
    GMEM_MOVEABLE = 0x0002
    
    global_mem = kernel32.GlobalAlloc(GMEM_MOVEABLE, len(data))
    global_data = kernel32.GlobalLock(global_mem)
    msvcrt.memcpy(ctypes.c_char_p(global_data), data, len(data))
    kernel32.GlobalUnlock(global_mem)
    user32.OpenClipboard(None)
    user32.EmptyClipboard()
    user32.SetClipboardData(CF_DIB, global_mem)
    user32.CloseClipboard()