[Win32utils-devel] Creating a clipboard monitor

Daniel Berger djberg96 at gmail.com
Thu Apr 23 15:42:57 EDT 2009


Heesob Park wrote:
> Hi,
> 
> 2009/4/23 Daniel Berger <djberg96 at gmail.com>:
>> Hi,
>>
>> I see the Win32::Clipboard module has a WaitForChange() method that waits
>> for a change in the Windows clipboard. I'd like to setup something similar
>> for the win32-clipboard library, perhaps call it notify_change to match
>> win32-eventlog.
>>
>> I looked at the implementation, and it's similar to others I found online.
>> But, it seems like it ought to be easier. Instead of RegisterClass(),
>> CreateWindow(), etc, can't we just use the handle of the current process
>> instead of injecting a new Window into the clipboard chain?
>>
>> In any case, I'm not having much luck trying to implement this. I did add
>> some methods to the Windows::Window::Classes module that might be necessary.
>> Any help here would be greatly appreciated.
>>
>> In other news, it seems GetWindowLongPtr and SetWindowLongPtr are not
>> exported by the runtime that ships with the one-click.
>>
>> Regards,
>>
>> Dan
>>
>> PS - It's a shame they don't provide a NotifyChangeClipboard() function to
>> make life easier. :)
>>
> As you expected, here is a sample code :)
> 
> require 'win32/api'
> require 'windows/clipboard'
> require 'windows/window/classes'
> require 'windows/window/message'
> require 'windows/library'
> require 'windows/window'
> require 'win32/clipboard'
> 
> include Win32
> include Windows::Window
> include Windows::Window::Classes
> include Windows::Window::Message
> include Windows::Library
> include Windows::Clipboard
> 
> handle = CreateWindow('static','test',0,0,0,0,0,0,0,0,0)
> 
> my_wnd_proc = API::Callback.new('LLLL', 'L') do |hwnd,uMsg,wParam,lParam|
>   case uMsg
>   when 0x0308 # WM_DRAWCLIPBOARD
>      puts 'Clipboard Changed'
>      puts Clipboard.data
>      next_viewer = GetWindowLongPtr(hwnd,GWL_USERDATA)
>      if next_viewer != 0
>         PostMessage(next_viewer,uMsg,wParam,lParam)
>      end
>      r = 0
>   when 0x030D # WM_CHANGECBCHAIN
>      puts 'WM_CHANGECBCHAIN'
>      next_viewer = lParam if next_viewer == wParam
> 	 if next_viewer != 0
> 	    PostMessage(next_viewer,uMsg,wParam,lParam)
> 	 end
> 	 r = 0
>   else
> 	 r = DefWindowProc(hwnd,uMsg,wParam,lParam)
>   end
>   r
> end
> 
> old_wnd_proc = SetWindowLongPtr(handle,GWL_WNDPROC,my_wnd_proc.address)
> next_viewer = SetClipboardViewer(handle)
> SetWindowLongPtr(handle,GWL_USERDATA,next_viewer)
> 
> msg = 0.chr * 100
> while true
>   while(PeekMessage(msg,handle,0,0,1))
>      TranslateMessage(msg)
>      DispatchMessage(msg)
>   end
>   sleep 0.1
> end
> 
> 
> In order to run this code, several changes are required.
> 1. GetWindowLongPtr and SetWindowLongPtr
> 
> In 32bit Windows, GetWindowLongPtr and SetWindowLongPtr are just a
> macro of GetWindowLog and SetWindowLong.
> Modify windows/window/classes.rb like this would be enough:
> 
>          begin
>             API.new('GetWindowLongPtr', 'LI', 'L', 'user32')
>             API.new('SetWindowLongPtr', 'LIP', 'L', 'user32')
>          rescue Win32::API::LoadLibraryError
>   	    alias :GetWindowLongPtr :GetWindowLong
> 	    alias :SetWindowLongPtr :SetWindowLong
>          end
> 
> 2. CreateWindow
> 
> In recent version of user32.dll, CreateWindow is not a function but a
> macro of CreateWindowEx.
> Modify windows/window.rb something like this
> 
> 	  begin
> 	     API.new('CreateWindow', 'PPLIIIILLLL', 'L', 'user32')
> 	  rescue Win32::API::LoadLibraryError
> 	     def CreateWindow(lpClassName, lpWindowName, dwStyle, x, y,
> nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
> 			CreateWindowEx(0,lpClassName, lpWindowName, dwStyle, x, y, nWidth,
> nHeight, hWndParent, hMenu, hInstance, lpParam)
> 		 end
> 	     def CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y,
> nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
> 			CreateWindowExA(0,lpClassName, lpWindowName, dwStyle, x, y, nWidth,
> nHeight, hWndParent, hMenu, hInstance, lpParam)
> 		 end
> 	     def CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,
> nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
> 			CreateWindowExW(0,lpClassName, lpWindowName, dwStyle, x, y, nWidth,
> nHeight, hWndParent, hMenu, hInstance, lpParam)
> 		 end
> 	  end
> 
> 3. GetUpdatedClipboardFormats
> GetUpdatedClipboardFormats is supported for Vista or later.
> Modify windows/clipboard.rb something like this
> 
>       begin
>          API.new('GetUpdatedClipboardFormats', 'PIP', 'I', 'user32')
>          API.new('AddClipboardFormatListener', 'L', 'B', 'user32')
>          API.new('RemoveClipboardFormatListener', 'L', 'B', 'user32')
>       rescue Win32::API::LoadLibraryError
>          # Windows Vista or later
>       end

Many thanks for this. I've made the changes in windows-pr (including the 
addition of DefProcName in Windows::Window::Message)

The only issue I had was that it always returns at least 1 notification 
when I first run the program, even when the clipboard is clear.

I could add an internal counter easily enough and only yield after the 
1st notification, but I wanted to make sure there wasn't a more 
appropriate solution first.

Regards,

Dan


More information about the win32utils-devel mailing list