SetClipboardViewer & Calculating CRC32 Clipboard Checksum...

Share your advanced PureBasic knowledge/code with the community.
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

SetClipboardViewer & Calculating CRC32 Clipboard Checksum...

Post by Teddy Rogers »

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...

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
Edit... changed key combination, added instructions on usage, minor code change.

Ted.