SetClipboardViewer & Calculating CRC32 Clipboard Checksum...
Posted: Mon Nov 03, 2014 4:18 am
I recently needed some PureBasic code in a program to calculate the CRC32 checksum of images in the clipboard and to monitor clipboard activity through window callback events. I did a quick search of the board and did not find much info on it so hopefully this will be of use to someone in the future. I have added quite a few comments for those who would like to peruse it, MSDN documents most of it quite thoroughly though. The CRC32 checksum routine seems to be quite fast and works for my needs - hopefully it works for you! Feel free to use and adapt it if you need it...
Edit... changed key combination, added instructions on usage, minor code change.
Ted.
Code: Select all
;
;
; ------------------------------------------------------------------
;
; PureBasic example code showing how to use; the clipboard viewer
; chain, calculate a CRC32 checksum of an image in the clipboard,
; detecting clipboard changes through window callback events,
; capturing owner and non-owner windows and saving the capture
; to clipboard.
;
; Press key "1" to capture window and CRC32 checksum
; Press key "2" to compare saved checksum with current clipboard
;
; By Teddy Rogers / PureBasic 5.22 LTS
;
; ------------------------------------------------------------------
;
; Declare the PrintWindow function from User32.dll
Prototype.i PrintWindow(hWnd, hdc, flag)
Global PrintWindow.PrintWindow
; Declare our procedures here...
Declare Snapshot()
Declare ClipboardChecksum()
Declare WindowCallback(WindowID.i, uMsg.l, wParam.l, lParam.l)
; This flag is used to prevent two drawing operations running at the same time after #WM_DRAWCLIPBOARD message is received from the callback...
; ...and we StartDrawing again to calculate the CRC32 checksum.
Global DrawingFinished = #False
; Save our CRC32 checksum values...
Global CRC32_1
Global CRC32_2
; Path and filename if you want to check what image(s) was drawn (enable disabled SaveImage code below)
Global FileName.s = "E:\Clipboard.bmp"
; Start the main window event...
If OpenWindow(0, 0, 0, 200, 150, "Window", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
; Added ourselves to the clipboard viewer chain using the SetClipboardViewer function...
; ...then start the window callback to start receiving window events.
MsgChain = SetClipboardViewer_(WindowID(0))
SetWindowCallback(@WindowCallback(), 0)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
; Politley remove ourselves from the chain of clipboard viewers by letting the next window know we are closing...
; ...and pass on the hWnd of the window in our message chain.
ChangeClipboardChain_(WindowID(0), MsgChain)
End
EndSelect
; Once our window is available we can capture the window and save it to the clipboard by pressing key 1...
; ...after it has been captured we can then calculate the CRC32 checksum of the image in the clipboard.
If GetAsyncKeyState_(#VK_1) & $8000 ; Key Press "1"
MessageBeep_(#MB_ICONINFORMATION) ; Sound a simple windows beep to notify that we captured a window
CRC32_1 = Snapshot()
If CRC32_1
Debug "Window CRC32_1 checksum calculated!"
EndIf
EndIf
; Press "2" key to calculate the CRC32 checksum of the image currently in the clipboard and compare it against our first checksum...
If (GetAsyncKeyState_(#VK_2) & $8000) ; Key Press "2"
MessageBeep_(#MB_ICONINFORMATION) ; Sound a simple windows beep to notify that we captured a window
CRC32_2 = ClipboardChecksum()
If CRC32_2
If CRC32_1 = CRC32_2
Debug "The clipboard contents match!"
Else
Debug "The clipboard contents do not match!"
EndIf
Debug "CRC32_1 = " + Hex(CRC32_1)
Debug "CRC32_2 = " + Hex(CRC32_2)
Else
Debug "Nothing to compare, clipboard empty or contains text?!"
EndIf
EndIf
ForEver
EndIf
; Capture our window using the PrintWindow function from User32.dll and save it to the clipboard
Procedure Snapshot()
Protected Checksum
If OpenLibrary(User32, "User32.dll")
PrintWindow = GetFunction(User32, "PrintWindow")
hWnd = WindowID(0)
;hWnd = FindWindow_(0,"Window") ; Enable this and change the window title if you want to capture another window
DrawingFinished = #False
If hWnd
If GetWindowRect_(hWnd, r.RECT) ; Get the window dimensions
If CreateImage(CapImage, r\right-r\left, r\bottom-r\top) ; Create an empty image to draw on and calculate the window size
hdc = StartDrawing(ImageOutput(CapImage))
; Call the PrintWindow function to capture the window contents and save it to the clipboard
If PrintWindow(hWnd, hdc, CapImage) ; Use PrintWindow function to capture the window image to "CapImage"
SetClipboardImage(CapImage) ; Save the new contents to the clipboard
; Calculate the length of the checksum from the image size
Checksum = CRC32Fingerprint((DrawingBuffer()), ((DrawingBufferPitch()) * ImageHeight(CapImage)))
EndIf
StopDrawing()
;SaveImage(CapImage, FileName.s) ; Enable this if you want to see the image
FreeImage(CapImage) ; Remember to free up the memory when done!
DeleteDC_(hdc) ; Delete any used device context
EndIf
EndIf
EndIf
DrawingFinished = #True
CloseLibrary(User32)
EndIf
ProcedureReturn Checksum
EndProcedure
; Calculate the CRC32 checksum of the data (IMAGE) in the clipboard.
; Other types clipboard contents such as text will naturally return #Null in "Checksum"
Procedure ClipboardChecksum()
Protected Checksum
; Quick delay to give time for clipboard contents to be updated...
; ...if you remove this sometimes the clipboard will be empty after being called from the window callback and CRC32 will return #Null
Delay(5)
Clipboard = GetClipboardImage(#PB_Any) ; Get the current clipboard contents
; If the clipboard contents are all good StartDrawing to "Clipboard". We need to start drawing only because...
; ...we need to know where in memory the contents are to calculate the CRC32 checksum. We find this by calling DrawingBuffer function
If Clipboard
If StartDrawing(ImageOutput(Clipboard))
Checksum = CRC32Fingerprint((DrawingBuffer()), ((DrawingBufferPitch()) * ImageHeight(Clipboard))) ; Calculate the length of the checksum from the image size
StopDrawing()
;SaveImage(Clipboard, FileName.s) ; Enable this if you want to see the image
FreeImage(Clipboard) ; Remember to free up the memory when done!
EndIf
EndIf
ProcedureReturn Checksum
EndProcedure
; WindowCallback procedure to listen for our chosen window events...
Procedure WindowCallback(WindowID.i, uMsg.l, wParam.l, lParam.l)
Select uMsg
; Only clipboard viewer windows receive this message. These are windows that have been added to the clipboard viewer chain by using the SetClipboardViewer function.
; Each window that receives the WM_DRAWCLIPBOARD message must call the SendMessage function to pass the message on to the next window in the clipboard viewer chain.
Case #WM_DRAWCLIPBOARD
Debug "Clipboard updated..."
SendMessage_(MsgChain, uMsg.l, wParam.l, lParam.l) ; Pass the message on to the next window to let them know the clipboard has been updated.
If DrawingFinished = #True ; If there are no other drawing operations going on calculate the CRC32 checksum.
CRC32_2 = ClipboardChecksum()
If CRC32_1
If CRC32_1 = CRC32_2
Debug "The clipboard contents match!"
Else
Debug "The clipboard contents do not match!"
EndIf
Debug "CRC32_1 = " + Hex(CRC32_1)
Debug "CRC32_2 = " + Hex(CRC32_2)
Else
Debug "Nothing To compare, clipboard empty or contains text?!"
EndIf
EndIf
; Sent to the first window in the clipboard viewer chain when a window is being removed from the chain.
; When a clipboard viewer window receives the WM_CHANGECBCHAIN message, it should call the SendMessage function to pass the message to the next window in the chain.
Case #WM_CHANGECBCHAIN
If wParam = MsgChain
MsgChain = lParam
Else
SendMessage_(MsgChain, uMsg.l, wParam.l, lParam.l)
EndIf
EndSelect
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Ted.