yasmin
yasmin

Reputation: 231

Copy to Clipboard in Ruby, HTML or C#

How do you copy text to the clipboard in Ruby?

Sounds simple right? But I've been trying to do this for 5 days now, with no success.

I searched on internet, but all I got is how to do it in newer versions of Ruby (I'm using 1.8.7 and no I can't use a newer version).

So I tried making a HTML file to do it for me. After trying 4-5 different ways (from online guides), in 3 browsers, and even looking at Photobucket's source code to try figuring how it copies img codes, I gave up. Nothing worked for me.

So I made a C# .exe and made my Ruby program call it. Finally something is being sent to the clipboard. It's a step forward, but still, it's only the first word in a string. When I try copying two words, only two is copied.

my Ruby program looks like this:

system  ("CopyClip.exe #{text}")

and in C# (in CopyClip), it does:

Clipboard.set_text(args[0])

Basically, I don't care if you help me do it in Ruby, HTML, C#, or any other language, as long as it works.

Upvotes: 23

Views: 18755

Answers (5)

Vitalii
Vitalii

Reputation: 1175

The clipboard gem allows you to access the clipboard on Liunx, MacOS and Windows. The focus is on simple text. You can copy a string with Clipboard.copy("string") and paste it with Clipboard.paste().
That's it, basically.

Now let's take a closer look at the specific platforms.

  • Linux For Linux support, the little utility xclip is needed. You can install it on Ubuntu with sudo apt-get install xclip.
    Furthermore, you can choose from which clipboard you want to paste (the default is PRIMARYCLIPBOARD). copy() copies to all the clipboards in Clipboard::CLIPBOARDS, fo example Clipboard.paste :primary.

  • macOS
    On the macOS, it works fine.

Upvotes: 6

starfry
starfry

Reputation: 9953

For those writing GTK+ applications (which the OP isn't), writing to the clipboard is quite straightforward:

Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD).set_text(content).store 

You can also use the primary (Gdk::Selection::PRIMARY) or secondary (Gdk::Selection::SECONDARY) X selections.

See also the full ruby API and its underlying C API.

Upvotes: 2

peter
peter

Reputation: 42192

Seems like you are using windows, this works with Ruby 1.9.3 under windows 7.

For the original answer, see Ruby copy to clipboard with Fiddle

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

In my collection I had another script that used to work in Windows 7 32 bit, if you have trouble with the first one and only use 32 bit, try this one

#!/usr/bin/env ruby -w
# win32 only

require 'singleton'
require 'thread'
require 'Win32API'

class Clipboard
  include Singleton

  CF_TEXT = 1

  def initialize
    @@mutex = Mutex.new
    @@open = Win32API.new("user32","OpenClipboard",['L'],'L')
    @@close = Win32API.new("user32","CloseClipboard",[],'L')
    @@empty = Win32API.new("user32","EmptyClipboard",[],'L')
    @@set = Win32API.new("user32","SetClipboardData",['L','P'],'L')
    @@get = Win32API.new("user32", "GetClipboardData", ['L'], 'L')
    @@lock = Win32API.new("kernel32", "GlobalLock", ['L'], 'P')
    @@unlock = Win32API.new("kernel32", "GlobalUnlock", ['L'], 'L')
  end

  def copy
    @@mutex.synchronize do
      @@open.Call(0)
      str = @@lock.Call(@@get.Call(CF_TEXT))
      @@unlock.Call(@@get.Call(CF_TEXT))
      @@close.Call
      return str
    end
  end

  def paste(str)
    @@mutex.synchronize do
      @@open.Call(0)
      @@empty.Call
      @@set.Call(CF_TEXT, str)
      @@close.Call
      @@lock = Win32API.new("kernel32", "GlobalLock", ['L'], 'P')
      @@unlock = Win32API.new("kernel32", "GlobalUnlock", ['L'], 'L')
      return nil
    end
  end
end

clip = Clipboard.instance
puts clip.copy
puts str
clip.paste("foo")
puts clip.copy

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: 0

Roman B.
Roman B.

Reputation: 3638

This answer works great for OSX:

def pbcopy(input)
 str = input.to_s
 IO.popen('pbcopy', 'w') { |f| f << str }
 str
end

def pbpaste
 `pbpaste`
end

Upvotes: 59

Jakob S
Jakob S

Reputation: 20125

I suspect if you wrap the argument to your C# app in quotes, you'll get all the text in args[0]:

system("CopyClip.exe '#{text}'")

Alternatively, and probably better in case your text contains apostrophes or other special shell characters:

system("CopyClip.exe", text)

Or perhaps even better, use the clipboard gem - this works for me on 1.8.7, but I haven't been able to test it on Windows:

require 'clipboard'
Clipboard.copy text

Upvotes: 7

Related Questions