Created
June 16, 2022 16:18
-
-
Save andyneff/73345a55e6f148e89aba93a575bdf997 to your computer and use it in GitHub Desktop.
Windows Clipboard Viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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