0

我有一个可以自动化用户的类 Automator。我在 Windows 中设置系统剪贴板时特别遇到问题。Automator 类使用 ClipSetThread 类,这是一个设置系统剪贴板的线程。ClipSetThread 的一个实例将一个线程作为输入,如果为空,它会加入(等待它完成)。

我觉得我没有正确调用 ClipSetThread,因为我在它的可靠性方面仍然存在我以前遇到的错误;在 ClipSetThread 之前。此代码在运行时不会抛出任何错误,但它的工作时间约为 2/3。其他时候它会打印 1134、_234 等。似乎线程没有加入(等待)彼此,或者被跳过。

代码:

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;

public class Automator extends Thread implements NativeMouseInputListener 
{
Robot rob = null;
TheAppClass theApp = null;
ClipSetThread lastClipSet = null; 
boolean doit = false;
boolean settingClip = false;

public void run()
{
    try // to make the Global hook
    {
        GlobalScreen.registerNativeHook();
    }
    catch (NativeHookException ex){theApp.updateOutput("No Global Keyboard or Mouse Hook");return;}
    try // to create a robot (can simulate user input such as mouse and keyboard input)
    {
        rob = new Robot();
    } 
    catch (AWTException e1) {theApp.updateOutput("The Robot could not be created");return;}

    while(true) {}
}

public void setApp(TheAppClass app)
{
    theApp = app;
    theApp.updateOutput("Succesfully started automator");
}

public void setClip(String arg)
{
    ClipSetThread set = new ClipSetThread(theApp, lastClipSet);
    lastClipSet = set;
    set.setClip(arg);
}

public void DOit()
{
    theApp.updateOutput("Starting");
    pasteAtCursorLocation("1");
    tab(1);
    pasteAtCursorLocation("2");
    tab(1);
    pasteAtCursorLocation("3");
    tab(1);
    pasteAtCursorLocation("4");
    tab(1);
    theApp.updateOutput("Complete");
}

public void nativeMouseReleased(NativeMouseEvent e) 
{
    //System.out.println("Mouse Released: " + e.getButton());
    if(doit)
    {
        DOit();
        doit = false;
    }
}

public void pasteAtCursorLocation(String text)
{
    setClip(text);
    rob.keyPress(KeyEvent.VK_CONTROL);
    rob.keyPress(KeyEvent.VK_V);
    rob.keyRelease(KeyEvent.VK_V);
    rob.keyRelease(KeyEvent.VK_CONTROL);
    theApp.updateOutput("Simulated Paste");
}
public void tab(int numTimes)
{
    while(numTimes > 0)
    {
        rob.keyPress(KeyEvent.VK_TAB);
        rob.keyRelease(KeyEvent.VK_TAB);
        numTimes--;
        theApp.updateOutput("Simulated Tab");
    }
}
// Unimplemented
public void nativeMouseClicked(NativeMouseEvent arg0) {}
public void nativeMousePressed(NativeMouseEvent arg0) {}
public void nativeMouseDragged(NativeMouseEvent arg0) {}
public void nativeMouseMoved(NativeMouseEvent arg0) {}
}

剪辑集线程:

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;


public class ClipSetThread extends Thread 
{
Clipboard sysClip = null;
TheAppClass  theApp = null;

public ClipSetThread(TheAppClass  app, Thread waitFor)
{
    theApp = app;
    sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
    if(waitFor != null)
        {try {waitFor.join();}catch (InterruptedException e) {}}
}
public void setClip(String arg)
{
    // Two strings that will hopefully never be on the clipboard
    String checkStr1 = "9999999999999";
    String checkStr2 = "99999999999999";
    // When we read in the clipboard we want to see if we change these strings from the ones they
    // will never be, if they do change we read the clipboard successfully
    String clipBoardTextBefore = checkStr1;
    String clipBoardTextAfter = checkStr2;

    // First get a copy of the current system clipboard text
    while(true)
    {
        try 
        {
            Transferable contents = sysClip.getContents(null);
            clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
        }
        catch(Exception e)
        {
            try {Thread.sleep(20);} catch (InterruptedException e1) {}
            continue;
        }
        break;
    }
    // If we failed to change the string it means we failed to read the text
    if(clipBoardTextBefore.equals(checkStr1)) 
        theApp.updateOutput("Could NOT get sysClip text");
    else
    {
        // If we didn't failed to get the current text try to change it
        while(true) 
        {
            try{sysClip.setContents(new StringSelection(arg), null);}
            catch(Exception e)
            {
                try {Thread.sleep(20);} catch (InterruptedException e1) {}
                continue;
            }
            break;
        }

        // Now again check to see the clipboard text
        while(true)
        {
            try 
            {
                Transferable contents = sysClip.getContents(null);
                clipBoardTextAfter = (String)contents.getTransferData(DataFlavor.stringFlavor);
            }
            catch(Exception e)
            {
                try {Thread.sleep(20);} catch (InterruptedException e1) {}
                continue;
            }
            break;
        }
        // If we failed to read the clipboard text
        if(clipBoardTextAfter.equals(checkStr2)) 
            theApp.updateOutput("Could NOT check if sysClip update was successful");
        else
        { // We re-read the clipboard text, see if it changed from the original clipboard text 
            if(clipBoardTextAfter.equals(checkStr1)) 
                theApp.updateOutput("Could NOT successfully set clipboard text");
            else
                theApp.updateOutput("Set Clipboard Text:" + arg + "\n");
        }
    }
}
}
4

2 回答 2

2

所以,首先,你永远不要调用start. ClipSetThread您还应该在加入之前检查线程是否还活着。

public class ClipSetThread extends Thread {

    Clipboard sysClip = null;
    TheAppClass theApp = null;

    private String toClipboard;

    public ClipSetThread(TheAppClass app, Thread waitFor, String toClipBoard) {
        theApp = app;
        sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
        this.toClipboard = toClipBoard;
        // !! Check to see if the thread is also alive before trying to join with it...
        if (waitFor != null && waitFor.isAlive()) {
            try {
                waitFor.join();
            } catch (InterruptedException e) {
            }
        }
    }

    // You should really put your logic into the `run` method in order to allow
    // the code to actually run in a separate thread...otherwise there is no
    // point in using a thread....
    @Override
    public void run() {
        // Two strings that will hopefully never be on the clipboard
        String checkStr1 = "9999999999999";
        String checkStr2 = "99999999999999";
        // When we read in the clipboard we want to see if we change these strings from the ones they
        // will never be, if they do change we read the clipboard successfully
        String clipBoardTextBefore = checkStr1;
        String clipBoardTextAfter = checkStr2;

        // First get a copy of the current system clipboard text
        while (true) {
            try {
                Transferable contents = sysClip.getContents(null);
                clipBoardTextBefore = (String) contents.getTransferData(DataFlavor.stringFlavor);
            } catch (Exception e) {
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e1) {
                }
                continue;
            }
            break;
        }
        // If we failed to change the string it means we failed to read the text
        if (clipBoardTextBefore.equals(checkStr1)) {
            theApp.updateOutput("Could NOT get sysClip text");
        } else {
            // If we didn't failed to get the current text try to change it
            while (true) {
                try {
                    sysClip.setContents(new StringSelection(toClipboard), null);
                } catch (Exception e) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e1) {
                    }
                    continue;
                }
                break;
            }

            // Now again check to see the clipboard text
            while (true) {
                try {
                    Transferable contents = sysClip.getContents(null);
                    clipBoardTextAfter = (String) contents.getTransferData(DataFlavor.stringFlavor);
                } catch (Exception e) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e1) {
                    }
                    continue;
                }
                break;
            }
            // If we failed to read the clipboard text
            if (clipBoardTextAfter.equals(checkStr2)) {
                theApp.updateOutput("Could NOT check if sysClip update was successful");
            } else { // We re-read the clipboard text, see if it changed from the original clipboard text 
                if (clipBoardTextAfter.equals(checkStr1)) {
                    theApp.updateOutput("Could NOT successfully set clipboard text");
                } else {
                    theApp.updateOutput("Set Clipboard Text:" + toClipboard + "\n");
                }
            }
        }
    }

}

根据我们之前的说法,使用它很危险while (true) {},也很浪费,因为它会不必要地消耗 CPU 周期......

public class Automator extends Thread implements NativeMouseInputListener {

    // A "locking" object...
    private static final Object WAIT_LOCK = new Object();

    Robot rob = null;
    TheAppClass theApp = null;
    ClipSetThread lastClipSet = null;
    boolean doit = false;
    boolean settingClip = false;

    public void run() {
        try // to make the Global hook
        {
            GlobalScreen.registerNativeHook();
        } catch (NativeHookException ex) {
            theApp.updateOutput("No Global Keyboard or Mouse Hook");
            return;
        }
        try // to create a robot (can simulate user input such as mouse and keyboard input)
        {
            rob = new Robot();
        } catch (AWTException e1) {
            theApp.updateOutput("The Robot could not be created");
            return;
        }

        // This is wasteful...
//        while (true) {
//        }
        // Locks do not consume CPU cycles while in the wait state...
        synchronized (WAIT_LOCK) {
            try {
                WAIT_LOCK.wait();
            } catch (Exception exp) {
            }
        }
    }

    public void dispose() {

        // Tell the thread it can terminate...
        synchronized (WAIT_LOCK) {
            WAIT_LOCK.notify();
        }
        // This will STOP the current thread (which called this method)
        // while the lastClipSet finishes...
        if (lastClipSet != null && lastClipSet.isAlive()) {
            lastClipSet.join();
        }

    }

    public void setClip(String arg) {
        ClipSetThread set = new ClipSetThread(theApp, lastClipSet, arg);
        lastClipSet = set;
        // You MUST START the thread...
        set.start();
    }

    /*...*/
}

更新

此代码可能会产生无限循环。如果剪贴板不包含String值会怎样?

while(true)
{
    try 
    {
        Transferable contents = sysClip.getContents(null);
        clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
    }
    catch(Exception e)
    {
        try {Thread.sleep(20);} catch (InterruptedException e1) {}
        continue;
    }
    break;
}

你经常这样做。我可能会建议您提供某种“转义”机制,以使其在多次重试后失败...

boolean successful = false;
int retries = 0;
while (!successful && retries < 20) {
{
    try 
    {
        Transferable contents = sysClip.getContents(null);
        clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
        successful = true;
    }
    catch(Exception e)
    {
        retries++;
        try {Thread.sleep(20);} catch (InterruptedException e1) {}
    }
}

更新了工作示例

好吧,那很有趣。我整理了一个(简单的)工作示例。您将需要打开某种文本编辑器。当您运行程序时,您有 5 秒的时间使其处于活动状态;)

我所做的唯一基本更改是我设置了在 250 毫秒的事件之间添加了一个自动延迟(请参阅rob.setAutoDelay(250).

现在,您也可以在每个关键事件之间设置延迟,使用Robot#delay,但这取决于您

public class Engine extends Thread {

    private Robot rob = null;
    private PasteThread lastClipSet = null;

    public void setClip(String arg) {
        if (lastClipSet != null && lastClipSet.isAlive()) {
            try {
                lastClipSet.join();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        PasteThread set = new PasteThread(arg);
        lastClipSet = set;
        lastClipSet.start();
    }

    public void pasteAtCursorLocation(String text) {
        System.out.println("Paste " + text);
        setClip(text);
        rob.keyPress(KeyEvent.VK_CONTROL);
        rob.keyPress(KeyEvent.VK_V);
        rob.keyRelease(KeyEvent.VK_V);
        rob.keyRelease(KeyEvent.VK_CONTROL);
    }

    public Engine() throws AWTException {
        rob = new Robot();
        rob.setAutoDelay(250);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
        }
        pasteAtCursorLocation("This is a simple test, thanks for watching!");
    }

    public static void main(String[] args) {
        try {
            new Engine();
        } catch (AWTException ex) {
            Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public class PasteThread extends Thread {

        private String toPaste;

        public PasteThread(String value) {

            toPaste = value;

        }

        @Override
        public void run() {
            Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
            System.out.println("Current clipboard contents = " + getClipboardContents(sysClip));
            sysClip.setContents(new StringSelection(toPaste), null);
            System.out.println("New clipboard contents = " + getClipboardContents(sysClip));
        }

        public String getClipboardContents(Clipboard clipboard) {
            String value = null;
            boolean successful = false;
            int retries = 0;
            while (!successful && retries < 20) {
                Transferable contents = clipboard.getContents(null);
                if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                    try {
                        value = (String) contents.getTransferData(DataFlavor.stringFlavor);
                        successful = true;
                    } catch (Exception exp) {
                        retries++;
                        exp.printStackTrace();
                    }
                } else {
                    retries++;
                }
            }
            System.out.println(successful + "/" + retries);
            return value;
        }
    }
}
于 2013-01-29T00:00:14.087 回答
1

您能否尝试重复粘贴操作,并在其间休眠 1 秒

public void pasteAtCursorLocation(String text)
{
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");

// put in a sleep 1 second here

rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
}

粘贴 2x 可能会产生不同的结果。这种奇怪行为的原因可能是 Windows 管理剪贴板的方式。如果粘贴 2x 剪贴板会给出不同的结果,那么您知道这种奇怪行为的根本原因不是在您的代码中找到,而是 Java 和 Windows 如何协同工作。

于 2013-01-28T23:54:24.433 回答