Skip to content

Instantly share code, notes, and snippets.

@andyneff
Created June 16, 2022 16:18
Show Gist options
  • Save andyneff/73345a55e6f148e89aba93a575bdf997 to your computer and use it in GitHub Desktop.
Save andyneff/73345a55e6f148e89aba93a575bdf997 to your computer and use it in GitHub Desktop.
Windows Clipboard Viewer
# Needs virtualenv with win32clipboard installed
import argparse
import hashlib
import ctypes
from ctypes import wintypes
from collections import OrderedDict
import win32clipboard
import pywintypes
CF_TYPES = {win32clipboard.CF_TEXT: 'CF_TEXT',
win32clipboard.CF_BITMAP: 'CF_BITMAP',
win32clipboard.CF_METAFILEPICT: 'CF_METAFILEPICT',
win32clipboard.CF_SYLK: 'CF_SYLK',
win32clipboard.CF_DIF: 'CF_DIF',
win32clipboard.CF_TIFF: 'CF_TIFF',
win32clipboard.CF_OEMTEXT: 'CF_OEMTEXT',
win32clipboard.CF_DIB: 'CF_DIB',
win32clipboard.CF_PALETTE: 'CF_PALETTE',
win32clipboard.CF_PENDATA: 'CF_PENDATA',
win32clipboard.CF_RIFF: 'CF_RIFF',
win32clipboard.CF_WAVE: 'CF_WAVE',
win32clipboard.CF_UNICODETEXT: 'CF_UNICODETEXT',
win32clipboard.CF_ENHMETAFILE: 'CF_ENHMETAFILE',
win32clipboard.CF_HDROP: 'CF_HDROP',
win32clipboard.CF_LOCALE: 'CF_LOCALE',
win32clipboard.CF_DIBV5: 'CF_DIBV5',
0x0080: 'CF_OWNERDISPLAY',
0x0081: 'CF_DSPTEXT',
0x0082: 'CF_DSPBITMAP',
0x008E: 'CF_DSPENHMETAFILE'}
CF_PRIVATEFIRST = 0x0200
CF_PRIVATELAST = 0x02FF
CF_GDIOBJFIRST = 0x0300
CF_GDIOBJLAST = 0x03FF
class BITMAPINFOHEADER(ctypes.Structure):
_fields_ = [
('biSize', wintypes.DWORD),
('biWidth', wintypes.LONG),
('biHeight', wintypes.LONG),
('biPlanes', wintypes.WORD),
('biBitCount', wintypes.WORD),
('biCompression', wintypes.DWORD),
('biSizeImage', wintypes.DWORD),
('biXPelsPerMeter', wintypes.LONG),
('biYPelsPerMeter', wintypes.LONG),
('biClrUsed', wintypes.DWORD),
('biClrImportant', wintypes.DWORD),
]
class RGBQUAD(ctypes.Structure):
_fields_ = [
('rgbRed', ctypes.c_byte),
('rgbGreen', ctypes.c_byte),
('rgbBlue', ctypes.c_byte),
('rgbReserved', ctypes.c_byte),
]
class BITMAPINFO(ctypes.Structure):
_fields_ = [
('bmiHeader', BITMAPINFOHEADER),
('bmiColors', ctypes.POINTER(RGBQUAD))
]
SIZEOF_BITMAPINFOHEADER = ctypes.sizeof(BITMAPINFOHEADER)
# kernel32 = ctypes.windll.kernel32
# kernel32.GlobalLock.argtypes = [ctypes.c_void_p]
# kernel32.GlobalLock.restype = ctypes.c_void_p
# kernel32.GlobalUnlock.argtypes = [ctypes.c_void_p]
# user32 = ctypes.windll.user32
# user32.GetClipboardData.restype = ctypes.c_void_p
def get_format_name(format):
if format > 0xC000:
return win32clipboard.GetClipboardFormatName(format)
elif format in CF_TYPES:
return CF_TYPES[format]
else:
return f'Unknown({format})'
def dump_clipboard(format):
print(format, get_format_name(format))
try:
data = win32clipboard.GetClipboardData(format)
except:
data = "Failed to GetClipboardData"
if format == win32clipboard.CF_DIB:
bmih = BITMAPINFOHEADER()
ctypes.memmove(ctypes.pointer(bmih), data, SIZEOF_BITMAPINFOHEADER)
print('biSize', bmih.biSize)
print('biWidth', bmih.biWidth)
print('biHeight', bmih.biHeight)
print('biPlanes', bmih.biPlanes)
print('biBitCount', bmih.biBitCount)
print('biCompression', bmih.biCompression)
print('biSizeImage', bmih.biSizeImage)
print('biXPelsPerMeter', bmih.biXPelsPerMeter)
print('biYPelsPerMeter', bmih.biYPelsPerMeter)
print('biClrUsed', bmih.biClrUsed)
print('biClrImportant', bmih.biClrImportant)
print(len(data), hashlib.md5(data).hexdigest())
elif format == win32clipboard.CF_DIBV5:
print('...', len(data), hashlib.md5(data).hexdigest())
else:
print(data)
def dump_clipboards():
try:
print(f'{win32clipboard.CountClipboardFormats()} found!')
except:
print('No clipboard formats available')
try:
win32clipboard.OpenClipboard(0)
# formats_available = win32clipboard.CountClipboardFormats()
format = win32clipboard.EnumClipboardFormats(0)
while format != 0:
dump_clipboard(format)
print()
format = win32clipboard.EnumClipboardFormats(format)
finally:
win32clipboard.CloseClipboard()
def clear_clipboard(clear_formats):
try:
win32clipboard.OpenClipboard(0)
# I can't figure out how to clear one format
# Copy the entire clipboard
clipboard = OrderedDict()
if clear_formats is not None:
format = win32clipboard.EnumClipboardFormats(0)
while format != 0:
if format not in clear_formats:
try:
clipboard[format] = win32clipboard.GetClipboardData(format)
except:
pass
format = win32clipboard.EnumClipboardFormats(format)
# Clear everything
win32clipboard.EmptyClipboard()
# re-set all but the format I wanted cleared
for format, data in clipboard.items():
try:
win32clipboard.SetClipboardData(format, data)
except:
print(f'Failed to set format: {format}')
finally:
win32clipboard.CloseClipboard()
def get_all_registered_formats(start=0xC000, stop=0xFFFF):
formats = {}
for x in range(start, stop+1):
try:
formats[x] = win32clipboard.GetClipboardFormatName(x)
except pywintypes.error:
pass
return formats
def get_available_formats():
formats = []
try:
win32clipboard.OpenClipboard(0)
# formats_available = win32clipboard.CountClipboardFormats()
format = win32clipboard.EnumClipboardFormats(0)
while format != 0:
formats.append(format)
format = win32clipboard.EnumClipboardFormats(format)
finally:
win32clipboard.CloseClipboard()
return formats
def main(args=None):
parser = argparse.ArgumentParser('Clipboard Viewer')
group = parser.add_mutually_exclusive_group()
group.add_argument('--formats', default=False, action='store_true')
group.add_argument('--list', default=False, action='store_true')
group.add_argument('--clear', type=int, default=[], nargs='+')
group.add_argument('--clear-all', default=False, action='store_true')
group.add_argument('--dump', default=None)
args = parser.parse_args(args)
if args.formats:
formats = get_all_registered_formats()
for form in formats:
print(f'{form}: {formats[form]}')
elif args.clear:
clear_clipboard(args.clear)
elif args.clear_all:
clear_clipboard(None)
elif args.list:
formats = get_available_formats()
print(f'{len(formats)} formats available')
for format in formats:
print(f'{format:5}: {get_format_name(format)}')
elif args.dump:
if isinstance(args.dump, str):
format = win32clipboard.RegisterClipboardFormat(args.dump)
else:
format = args.dump
try:
win32clipboard.OpenClipboard(0)
dump_clipboard(format)
finally:
win32clipboard.CloseClipboard()
else:
dump_clipboards()
if __name__ == '__main__':
main()
# if user32.c(v):
# print(f'There is currently data in {k}({v}):')
# # print(type(user32.GetClipboardData(v)))
# user32.OpenClipboard(0)
# try:
# data = user32.GetClipboardData(v)
# data_locked = kernel32.GlobalLock(data)
# text = ctypes.c_char_p(data_locked)
# value = text.value
# kernel32.GlobalUnlock(data_locked)
# print(value)
# finally:
# user32.CloseClipboard()
# def get_clipboard_text():
# user32.OpenClipboard(0)
# try:
# if user32.IsClipboardFormatAvailable(CF_TEXT):
# data = user32.GetClipboardData(CF_TEXT)
# data_locked = kernel32.GlobalLock(data)
# text = ctypes.c_char_p(data_locked)
# value = text.value
# kernel32.GlobalUnlock(data_locked)
# return value
# finally:
# user32.CloseClipboard()
# print(get_clipboard_text())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment