Java / Swing: propiedad del portapapeles del sistema

I'm writing a small Java program that's supposed to run an external program that copies an image to the system clipboard (i.e. the Windows 7 "snipping tool"), wait for it to finish, save the image from the clipboard to disk and copy a URL (from which the image can be accessed) to clipboard. In short, it is supposed to:

  1. run external tool and wait for it
  2. copy an image from clipboard
  3. copy a string to clipboard

This, my program is perfectly able to do. However, I would like to use Swing/AWT to present a user interface. I'm using a system tray icon, but for simplicity's sake, it could just as well be a JButton in a frame. When the button is clicked, the process above should be carried out. The first time this is done, it works as it should. The image is copied, pasted to disk and the string is copied to clipboard. Then, the second time the button is clicked, it is as though my program does not realize that the clipboard has been updated, as it is still seeing its own string from the first time around. Only afterwards does my clipboard handling class lose ownership, and in effect, every second attempt at the procedure fails.

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JFrame;

public class Main {
    private static BufferedImage image; //the image from clipboard to be saved

    public static void main(String[] args) throws InterruptedException, IOException {
        new GUI();

    public static void run(String filename) throws IOException, InterruptedException {
        CBHandler cbh = new CBHandler();

        //run tool, tool will copy an image to system clipboard
        Process p = Runtime.getRuntime().exec("C:\\Windows\\system32\\SnippingTool.exe");

        //copy image from clipboard
        image = cbh.getClipboard();
        if(image == null) {
            System.out.println("No image found in clipboard.");

        //save image to disk...

        //copy file link to clipboard
        String link = "" + filename;

class CBHandler implements ClipboardOwner {
    public BufferedImage getClipboard() {
        Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);

        try {
                return (BufferedImage) t.getTransferData(DataFlavor.imageFlavor);
        catch(Exception e) {
        return null;

    public void setClipboard(String str) {
        StringSelection strsel = new StringSelection(str);
        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(strsel, this);

    public void lostOwnership(Clipboard arg0, Transferable arg1) {
        System.out.println("Lost ownership!");

class GUI extends JFrame {
    public GUI() {
        JButton button = new JButton("Run");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                try {
                } catch (IOException e) {
                } catch (InterruptedException e) {


If you try running it, notice that on the second run, the lostOwnership method is only called AFTER the attempt at copying the image. I'm guessing this is the source of my problem, and I have no idea why it is happening, except that it only happens when triggered by a Swing event. Any help solving this is appreciated.

preguntado el 27 de agosto de 11 a las 17:08

2 Respuestas

One guess: You are doing your whole handling (calling the other process) on the AWT event dispatch thread (e.g. directly from the ActionListener or similar).

The Clipboard change messages will also be handled by the VM on the EDT ... but only after your button-click is done.

The moral: Don't do long-running stuff (and stuff which has effects which should be enqueued in the event queue) on the EDT - instead, start a new thread for this.

Respondido 28 ago 11, 00:08

Could you give me an example (pseudo-code or otherwise) on how to accomplish this in my scenario? - Con

The key to understanding the lost ownership issue is in this line

Toolkit.getDefaultToolkit().getSystemClipboard().setContents(strsel, this);

The second parameter you are passing in is ClipboardOwner. JavaDocs for clipboard.setContents says

If there is an existing owner different from the argument owner, that owner is notified that it no longer holds ownership of the clipboard contents via an invocation of ClipboardOwner.lostOwnership() on that owner. An implementation of setContents() is free not to invoke lostOwnership() directly from this method. For example, lostOwnership() may be invoked later on a different thread. The same applies to FlavorListeners registered on this clipboard.

Okay so what's happening? When you pass in the owner, Clipboard now has a reference to that object. In this case it's CBHandler. You then create a new one and attempt to set the contents again. Clipboard then goes back to the old owner (your original instance) and tells it "Hey, your not the owner anymore".

public synchronized void setContents(Transferable contents, ClipboardOwner owner) {
    final ClipboardOwner oldOwner = this.owner;
    final Transferable oldContents = this.contents;

    this.owner = owner;
    this.contents = contents;

    if (oldOwner != null && oldOwner != owner) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                oldOwner.lostOwnership(Clipboard.this, oldContents);

You'll have to provide more details on the other issue "it is as though my program does not realize that the clipboard has been updated, as it is still seeing its own string from the first time around. "

Respondido 28 ago 11, 00:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.