Reputation: 539
I am trying to copy a string to the clipboard with the Fiddle module,but this code fails.
require'fiddle';
user32 = Fiddle.dlopen 'USER32.dll';
$openClipboard = Fiddle::Function.new(user32['OpenClipboard'],[Fiddle::TYPE_INT],Fiddle::TYPE_INT);
$closeClipboard = Fiddle::Function.new(user32['CloseClipboard'],[],Fiddle::TYPE_INT);
$emptyClipboard = Fiddle::Function.new(user32['EmptyClipboard'],[],Fiddle::TYPE_INT);
$setClipboardData = Fiddle::Function.new(user32['SetClipboardData'],[Fiddle::TYPE_INT,Fiddle::TYPE_VOIDP],Fiddle::TYPE_VOIDP);
class Clipboard
def initialize
@closed = false;
puts "openClipboard : #{$openClipboard.call(0)}";
puts "emptyClipboard : #{$emptyClipboard.call}";
end
def data=(d)
return 'Cannot write to closed clipboard' if @closed;
puts "setClipboardData : #{$setClipboardData.call(1,d)}"; # 1 is CF_TEXT
end
def close
return 'Already closed' if @closed;
@closed = true;
puts "closeClipboard : #{$closeClipboard.call}";
end
end
c = Clipboard.new
puts 'going to write';
gets;
c.data = 'red'; # Should write 'red' to the clipboard
p 'after writing to the clipboard';
gets;
c.close;
p 'closed';
gets
But it's failing in the #data= method. (This is just an attempt to translate the code at http://www.codeproject.com/Articles/2242/Using-the-Clipboard-Part-I-Transferring-Simple-Tex). Any idea on how to do it without using an external gem/library?
Upvotes: 1
Views: 759
Reputation: 42207
Can't help you with fiddle because Ruby 1.9.3 doesn't support the dlopen method. In my library of scripts I found this working example. Found it at https://github.com/janlelis/clipboard/blob/master/lib/clipboard/windows.rb
Can't find open3 in my list of gems so I suppose it is part of the standard library. In any way, to interact at such level with the OS you will need some kind of gem, incorporated or not, fiddle is also a gem.
require 'open3'
module Clipboard; end
module Clipboard::Windows
extend self
CF_TEXT = 1
CF_UNICODETEXT = 13
GMEM_MOVEABLE = 2
# get ffi function handlers
begin
require 'ffi'
rescue LoadError
raise LoadError, 'Could not load the required ffi gem, install it with: gem install ffi'
end
module User32
extend FFI::Library
ffi_lib "user32"
ffi_convention :stdcall
attach_function :open, :OpenClipboard, [ :long ], :long
attach_function :close, :CloseClipboard, [ ], :long
attach_function :empty, :EmptyClipboard, [ ], :long
attach_function :get, :GetClipboardData, [ :long ], :long
attach_function :set, :SetClipboardData, [ :long, :long ], :long
end
module Kernel32
extend FFI::Library
ffi_lib 'kernel32'
ffi_convention :stdcall
attach_function :lock, :GlobalLock, [ :long ], :pointer
attach_function :unlock, :GlobalUnlock, [ :long ], :long
attach_function :size, :GlobalSize, [ :long ], :long
attach_function :alloc, :GlobalAlloc, [ :long, :long ], :long
end
# see http://www.codeproject.com/KB/clipboard/archerclipboard1.aspx
def paste(_ = nil)
ret = ""
if 0 != User32.open( 0 )
hclip = User32.get( CF_UNICODETEXT )
if hclip && 0 != hclip
pointer_to_data = Kernel32.lock( hclip )
data = ""
# Windows Unicode is ended by to null bytes, so get the whole string
size = Kernel32.size( hclip )
data << pointer_to_data.get_bytes( 0, size - 2 )
if RUBY_VERSION >= '1.9'
ret = data.force_encoding("UTF-16LE").encode(Encoding.default_external) # TODO catch bad encodings
else # 1.8: fallback to simple CP850 encoding
require 'iconv'
utf8 = Iconv.iconv( "UTF-8", "UTF-16LE", data)[0]
ret = Iconv.iconv( "CP850", "UTF-8", utf8)[0]
end
if data && 0 != data
Kernel32.unlock( hclip )
end
end
User32.close( )
end
ret || ""
end
def clear
if 0 != User32.open( 0 )
User32.empty( )
User32.close( )
end
paste
end
def copy(data_to_copy)
if ( RUBY_VERSION >= '1.9' ) && 0 != User32.open( 0 )
User32.empty( )
data = data_to_copy.encode("UTF-16LE") # TODO catch bad encodings
data << 0
handler = Kernel32.alloc( GMEM_MOVEABLE, data.bytesize )
pointer_to_data = Kernel32.lock( handler )
pointer_to_data.put_bytes( 0, data, 0, data.bytesize )
Kernel32.unlock( handler )
User32.set( CF_UNICODETEXT, handler )
User32.close( )
else # don't touch anything
Open3.popen3( 'clip' ){ |input,_,_| input << data_to_copy } # depends on clip (available by default since Vista)
end
paste
end
end
Clipboard::Windows.copy("test")
puts Clipboard::Windows.paste
If you don't mind to install a gem, here is a much simpler solution, works on windows7 64 bit, Ruby 1.9.3.
#gem install clipboard
require 'clipboard'
Clipboard.copy("This is a sentence that has been copied to your clipboard")
puts Clipboard.paste
Upvotes: 2