前言&摸索历程
之前看到Bing Rewards能赚一些东西,每天差不多能赚个1块多钱,但是又要求我天天使用必应搜索,而且还是手机端和电脑端都要。我肯定不能每天这么勤勤恳恳地做啊,所以我就想着能不能整个什么东西让它自己来跑。
欸,正好,我之前有一台旧电脑,CPU是i3-3220,内存有8G,让它运行这个应该很不错。系统上,我起初安装的是Windows 8.1,奈何到2024年10月,我安装的安卓模拟器没什么好使的,于是我转向了Linux。我希望用Waydroid来代替一众安卓模拟器,于是我最开始安装了KUbuntu。Waydroid在KDE上的表现并没有我预想中的这么好,会有一些Bug,比如安卓容器内的鼠标位置和外面KDE的鼠标位置不重合,会有一定的偏差。后来我换用Ubuntu,心想GNOME配Wayland使用Waydroid应该很丝滑吧,事实上我错了。Waydroid这回倒是运行得很正常,倒是我写自动化脚本的时候捉急了。很多自动化的功能在Wayland下运行不正常!甚至连鼠标的位置都没有什么好的方法获取,遂作罢。
最后,我使用了Windows 10加上WSA的组合,用下来发现这才是最顺滑的。虽然一直在说Windows屎山,但是方便也是真方便啊。经过一番折腾,便有了以下结果。
准备
这套系统需要这些东西。东西如何配置将在后面的章节说明。
- 一台能安装Windows 10和WSA且流畅不卡顿的电脑
- 新版Edge浏览器和WSA(安卓安装Via浏览器)
- ADB 安卓调试桥
- Java 21
- C++编译器(比如VS的MSVC)
- 电脑开机卡(或者用Wake On Lan代替)
- 显卡欺骗器(Windows 10有软件的虚拟显示器,大家可以试试)
预期
- 这套系统通过电脑开机卡,使电脑每天自动启动。你也可以通过WOL或自己每天辛苦一下开个机代替。
- 电脑中的Windows 10系统启动后自动启动我的Java程序,通过Java程序自动打开浏览器,完成Bing Rewards任务,并在结束任务后自动关机。
实战
安装系统和必要软件
首先安装Windows 10,这个应该没什么太大问题。新版Edge浏览器是后续更新后的Windows 10的默认浏览器,所以Edge不需要额外安装。
然后安装Java 21,安装Java的教程也很多,这个问题也不大。
其次是安装WSA,这个我推荐使用Github上有人制作的安装包,链接。选择自己对应的版本后下载,然后将下载的文件解压到一个文件夹,路径最好是全英文的。解压后,里面会有一个Run.bat的批处理文件。先在系统的控制面板->程序->启用或关闭Windows功能中启用虚拟机平台,然后重启Windows系统,最后运行刚才的Run.bat完成WSA的安装。
WSA安装好之后,在开始菜单中找到“适用于Android™的Windows子系统”,在那里面把开发人员选项打开,然后通过adb连接WSA,通过命令行cmd输入adb connect 127.0.0.1:58526
。连接成功后,下载一个Via的安装包,然后通过命令adb install via.apk
安装Via浏览器。
最后为Edge浏览器和Via浏览器安装上对应的挂机脚本,这个脚本不是我的,原作者提供了视频和教程,见B站。
为Java编写JNI dll库
由于我是使用的Java来编写的,Windows API并不能直接在Java中访问,而我需要Windows API来对窗口进行一些调整。 编译后的JNI dll可以在这里下载。以下是dll的创建方法: 在VS中新建一个dll项目,然后在dllmain.cpp中这样写:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include
#include
#include
#include
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" {
//java函数WindowManager.moveWindow(long hwnd, int x, int y)的实现.移动一个窗口.
JNIEXPORT void JNICALL Java_main_WindowManager_moveWindow(JNIEnv* env, jobject obj, jlong hwnd, jint x, jint y) {
SetWindowPos((HWND)hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
//java函数WindowManager.resizeWindow(long hwnd, int width, int height)的实现.调整一个窗口的大小.
JNIEXPORT void JNICALL Java_main_WindowManager_resizeWindow(JNIEnv* env, jobject obj, jlong hwnd, jint width, jint height) {
SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER);
}
//用于C++ WindowsAPI获取所有窗口句柄时的回调函数,仅在下面Java函数WindowManager.getAllWindow()的实现中使用.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
std::vector* windowHandles = reinterpret_cast<std::vector*>(lParam);
windowHandles->push_back(reinterpret_cast(hwnd));
return TRUE;
}
//java函数WindowManager.getAllWindow()的实现.获取所有窗口的句柄并以long数组的形式返回.
JNIEXPORT jlongArray JNICALL Java_main_WindowManager_getAllWindow(JNIEnv* env, jobject obj) {
std::vector windowHandles;
//通过C++ WindowsAPI获取所有窗口.
EnumWindows(EnumWindowsProc, reinterpret_cast(&windowHandles));
//创建Java long数组.
jlongArray result = env->NewLongArray(windowHandles.size());
if (result == nullptr) {
return nullptr; //内存不足.
}
//将C++中的数据复制进Java数组.
env->SetLongArrayRegion(result, 0, windowHandles.size(), windowHandles.data());
return result;
}
//java函数WindowManager.getWindowTitle(long hwnd)的实现.获取窗口的标题.
JNIEXPORT jstring JNICALL Java_main_WindowManager_getWindowTitle(JNIEnv* env, jobject obj, jlong hwnd) {
wchar_t buffer[256];
//获取窗口标题.
int length = GetWindowTextW((HWND)hwnd, buffer, 256);
if (length > 0) {
//将C++字符串转为Java字符串.
return env->NewString((const jchar*)buffer, length);
}
else {
//获取标题失败.
return nullptr;
}
}
//java函数WindowManager.getNativeWindowPosition(long hwnd)的实现.获取窗口的位置.
JNIEXPORT jintArray JNICALL Java_main_WindowManager_getNativeWindowPosition(JNIEnv* env, jobject obj, jlong hwnd) {
RECT rect;
if (GetWindowRect((HWND)hwnd, &rect)) {
//创建Java int数组.
jintArray result = env->NewIntArray(2);
if (result == nullptr) {
return nullptr; //内存不足
}
//设置数组内容.
jint pos[2] = { rect.left, rect.top };
env->SetIntArrayRegion(result, 0, 2, pos);
return result;
}
else {
//获取位置失败.
return nullptr;
}
}
//java函数WindowManager.getNativeWindowSize(long hwnd)的实现.获取窗口的大小.
JNIEXPORT jintArray JNICALL Java_main_WindowManager_getNativeWindowSize(JNIEnv* env, jobject obj, jlong hwnd) {
RECT rect;
if (GetWindowRect((HWND)hwnd, &rect)) {
//创建Java int数组.
jintArray result = env->NewIntArray(2);
if (result == nullptr) {
return nullptr; //内存不足
}
//设置数组内容.
jint size[2] = { rect.right - rect.left, rect.bottom - rect.top };
env->SetIntArrayRegion(result, 0, 2, size);
return result;
}
else {
//获取大小失败.
return nullptr;
}
}
//java函数WindowManager.bringToFront(long hwnd)的实现.把窗口放到最前并聚焦(不是置顶).
JNIEXPORT void JNICALL Java_main_WindowManager_bringToFront(JNIEnv* env, jobject obj, jlong hwnd) {
// 获取当前线程ID
DWORD currentThreadId = GetCurrentThreadId();
// 获取前台窗口的线程ID
DWORD foregroundThreadId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
// 如果当前线程不是前台线程,附加到前台线程
if (currentThreadId != foregroundThreadId) {
AttachThreadInput(currentThreadId, foregroundThreadId, TRUE);
}
// 将窗口带到前台
if (!SetForegroundWindow((HWND)hwnd)) {
//懒得解决编码问题了,这里输出使用英文(下面的都是):
std::cerr << "Failed to bring window to front. Error: " << GetLastError() << std::endl;
}
// 恢复线程输入状态
if (currentThreadId != foregroundThreadId) {
AttachThreadInput(currentThreadId, foregroundThreadId, FALSE);
}
}
//java函数WindowManager.setWindowFront(long hwnd)的实现.置顶窗口.
JNIEXPORT void JNICALL Java_main_WindowManager_setWindowFront(JNIEnv* env, jobject obj, jlong hwnd) {
if (!SetWindowPos((HWND)hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) {
std::cerr << "Failed to set window front. Error: " << GetLastError() << std::endl;
}
}
//java函数WindowManager.unsetWindowFront(long hwnd)的实现.取消置顶窗口.
JNIEXPORT void JNICALL Java_main_WindowManager_unsetWindowFront(JNIEnv* env, jobject obj, jlong hwnd) {
if (!SetWindowPos((HWND)hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) {
std::cerr << "Failed to unset window front. Error: " << GetLastError() << std::endl;
}
}
}
在项目的属性中,在 C/C++ -> 代码生成 中设置运行库为多线程调试(/MTd),然后在 VC++目录 中的 包含目录 添加如下:
%JAVA_HOME%/include
%JAVA_HOME%/include/win32
其中%JAVA_HOME%是你安装的Java的目录,这样操作之后VS才能知道jni.h和一堆的JNI有关的东西是从哪里来的。 生成WindowManager,并保存好产生的WindowManager.dll,这就是我刚才通过网络分享的文件。
在Java中编写JNI实现
整个Java项目没有使用到任何依赖,但使用了Maven。这是Java项目的文件列表。
├─.idea //IDEA的配置文件,不管
├─src
│ ├─main
│ │ ├─java
│ │ │ └─main
│ │ │ ├─Main.java //java项目的主要文件,业务代码放在这里
│ │ │ ├─Robot.java //为Main.java提供的一些方便的函数(本文的例子中里面的方法没有全部被使用)
│ │ │ └─WindowManager.java //这一步需要编写的java代码,里面的部分函数本文并未使用到,但你或许需要这些.
│ │ └─resources
│ │ └─WindowManager.dll //上一步中获得的dll文件,请复制到这里
│ └─test
│ └─java
├─.gitignore
└─pom.xml
这一步我们来编写WindowManager类来实现JNI方法。代码见下:
package main;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
/**
* 通过JNI控制Windows的窗口(不跨平台).此类中关于窗口位置和窗口大小的函数并不是精确的!
* @author Denvo
* @version 0.0.1
*/
public class WindowManager {
public WindowManager() {
//确定dll文件是否存在.
File dllFile = new File(Main.JAR_PATH + "/WindowManager.dll");
if (!dllFile.exists()) {
try {
Files.copy(WindowManager.class.getResourceAsStream("/WindowManager.dll"), dllFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//尝试加载dll库.
System.load(dllFile.getPath());
}
/**
* 移动一个窗口到指定的位置.
* @param hwnd 需要移动的窗口
* @param x 移动到的目标位置
* @param y 移动到的目标位置
*/
public native void moveWindow(long hwnd, int x, int y);
/**
* 控制一个窗口的大小.
* @param hwnd 需要控制的窗口
* @param width 目标宽度
* @param height 目标高度
*/
public native void resizeWindow(long hwnd, int width, int height);
/**
* 获取Windows上的所有窗口.
* @return 将所有窗口的句柄以long的类型返回
*/
public native long[] getAllWindow();
/**
* 获取一个窗口的标题.
* @param hwnd 目标窗口的句柄
* @return 窗口标题
*/
public native String getWindowTitle(long hwnd);
/**
* 获取窗口的坐标.
* @param hwnd 目标窗口
* @return 窗口的坐标
*/
public int[] getWindowPosition(long hwnd) {
int[] result = getNativeWindowPosition(hwnd);
//由于关于坐标和大小的函数的返回值貌似不是很精确,只能通过这种方式勉强弥补一下,说白了就是不要乱动这里的+8和下面的-16.
result[0] = result[0] + 8;
result[1] = result[1] + 8;
return result;
}
/**
* native代码,在getWindowPosition中调用.
* @param hwnd 窗口
* @return 窗口位置
*/
private native int[] getNativeWindowPosition(long hwnd);
/**
* 获取窗口的大小.
* @param hwnd 目标窗口
* @return 窗口的大小(宽度,高度)
*/
public int[] getWindowSize(long hwnd) {
int[] result = getNativeWindowSize(hwnd);
//原因见getWindowPosition(long hwnd)方法.
result[0] = result[0] -16;
result[1] = result[1] -16;
return result;
}
/**
* native代码,在getWindowSize中调用.
* @param hwnd 窗口
* @return 窗口大小
*/
private native int[] getNativeWindowSize(long hwnd);
/**
* 使一个窗口被放到最前端(不是置顶)并聚焦(获得焦点).
* 能使任务栏上的图标发出橙色闪烁,提示需要查看这个窗口.
* 只想使窗口放到最前建议使用bringWindowFront()方法.
* @param hwnd 目标窗口
*/
public native void bringToFront(long hwnd);
/**
* 置顶窗口.
* @param hwnd 目标窗口
*/
public native void setWindowFront(long hwnd);
/**
* 取消置顶窗口.
* @param hwnd 目标窗口
*/
public native void unsetWindowFront(long hwnd);
/**
* 使一个窗口被放到最前端(不是置顶)并聚焦(获得焦点).
* 一般情况使用此方法.如需使任务栏图标闪烁提示,请使用bringToFront()方法.
* @param hwnd 目标窗口
*/
public void bringWindowFront(long hwnd) {
setWindowFront(hwnd);
try {
Thread.sleep(10);
unsetWindowFront(hwnd);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
编写一些方便的函数
我在Robot类中写了一些方便的函数,内容是这样的:
package main;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 操作鼠标和键盘自动化的类.此类中所有的方法都是单线程(注释中标注的除外),可能会导致线程堵塞!
* @author Denvo
* @version 0.0.1
*/
public class Robot {
private final java.awt.Robot robot;
{
try {
robot = new java.awt.Robot();
robot.setAutoDelay(66);
} catch (AWTException e) {
throw new RuntimeException(e);
}
}
/**
* 点击屏幕上某一个位置.
* @param x 需要点击的位置的坐标
* @param y 需要点击的位置的坐标
*/
public void click(int x, int y) {
robot.mouseMove(x, y);
try {
Thread.sleep(66);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(66);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(66);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 通过模拟键盘敲击的方式输入文字.只支持大小写字母、阿拉伯数字以及实体键盘上有的符号!
* @param str 需要输入的文字
*/
public void input(String str) {
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (Character.isLowerCase(chars[i])) {
//判断字符是否是小写字母
pressKey(KeyEvent.getExtendedKeyCodeForChar(chars[i]));
} else if (Character.isUpperCase(chars[i])) {
//判断字符是否是大写字母
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.getExtendedKeyCodeForChar(Character.toLowerCase(chars[i])));
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if (Character.isDigit(chars[i])) {
//判断字符是否为阿拉伯数字
pressKey(KeyEvent.getExtendedKeyCodeForChar(chars[i]));
}else if (' ' == chars[i]) {
//判断是否是空格
pressKey(KeyEvent.VK_SPACE);
} else if (',' == chars[i]) {
//后续都是各种符号的判断
pressKey(KeyEvent.VK_COMMA);
} else if ('.' == chars[i]) {
pressKey(KeyEvent.VK_PERIOD);
}else if ('/' == chars[i]) {
pressKey(KeyEvent.VK_SLASH);
} else if (';' == chars[i]) {
pressKey(KeyEvent.VK_SEMICOLON);
} else if ('\'' == chars[i]) {
pressKey(KeyEvent.VK_QUOTE);
} else if ('[' == chars[i]) {
pressKey(KeyEvent.VK_OPEN_BRACKET);
} else if (']' == chars[i]) {
pressKey(KeyEvent.VK_CLOSE_BRACKET);
} else if ('\\' == chars[i]) {
pressKey(KeyEvent.VK_BACK_SLASH);
} else if ('`' == chars[i]) {
pressKey(KeyEvent.VK_BACK_QUOTE);
} else if ('-' == chars[i]) {
pressKey(KeyEvent.VK_MINUS);
} else if ('=' == chars[i]) {
pressKey(KeyEvent.VK_EQUALS);
} else if ('~' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_BACK_QUOTE);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('!' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_1);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('@' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_2);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('#' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_3);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('$' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_4);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('%' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_5);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('^' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_6);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('&' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_7);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('*' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_8);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('(' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_9);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if (')' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_0);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('_' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_MINUS);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('+' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_EQUALS);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('{' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_OPEN_BRACKET);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('}' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_CLOSE_BRACKET);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('|' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_BACK_SLASH);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if (':' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_SEMICOLON);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('"' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_QUOTE);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('<' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_COMMA);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('>' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_PERIOD);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else if ('?' == chars[i]) {
robot.keyPress(KeyEvent.VK_SHIFT);
pressKey(KeyEvent.VK_SLASH);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else {
throw new RuntimeException("键盘上未找到对应的字符:" + chars[i]);
}
}
}
/**
* 按一次键盘上特定的键.
* @param keycode 要按下的按键的keycode
*/
public void pressKey(int keycode) {
robot.waitForIdle();
robot.keyPress(keycode);
try {
Thread.sleep(66);
robot.keyRelease(keycode);
Thread.sleep(66);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 执行一段命令行.运行的命令将在新建的虚拟线程内执行,所有输出都将通过System.out.println()方法打印.
* @param cmd 需要执行的命令行.
* @param workDir 如果需要工作目录,则输入目录的路径;传入null则使用jar文件的目录.
*/
public void run(String cmd, String workDir) {
String[] cmd_slipt = cmd.split(" ");
ProcessBuilder processBuilder;
String charset;
//对跨平台(Windows/Linux/MacOS)处理.虽然本程序只打算用于Windows.
if (System.getProperty("os.name").toLowerCase().contains("win")) {
processBuilder = new ProcessBuilder("cmd", "/c");
charset = "GBK";
} else {
processBuilder = new ProcessBuilder("/bin/sh", "-c");
charset = "UTF-8";
}
for (int i = 0; i < cmd_slipt.length; i++) {
processBuilder.command().add(cmd_slipt[i]);
}
//对工作目录的处理.
if (workDir == null) {
processBuilder.directory(new File(Main.JAR_PATH));
} else {
processBuilder.directory(new File(workDir));
}
//合并输出流.
processBuilder.redirectErrorStream(true);
//启动进程.
Thread.startVirtualThread(() -> {
try {
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), charset));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
System.out.println("命令执行结束.退出代码为 " + process.waitFor());
process.destroy();
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
这个java项目并没有设计日志等功能,它只能勉强满足使用。
编写Java主类实现自动化
所有的自动化的步骤和逻辑在这里完成。这个每个人要根据自己的需求来改,这是我的,就是按着顺序点击各个地方,然后等待一段时间自动关机。
package main;
import java.io.File;
/**
* AutoRunner的主类.
* @author Denvo
* @version 0.0.1
*/
public class Main {
public static final String JAR_PATH = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getFile()).getParent();
public static void main(String[] args) throws InterruptedException {
//前面如果需要处理什么内容...
Robot robot = new Robot();
WindowManager wm = new WindowManager();
Thread.sleep(3000);
//启动电脑端edge浏览器.
robot.run("\"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\" cn.bing.com", null);
Thread.sleep(8000);
long[] hwnds = wm.getAllWindow();
for (int i = 0; i < hwnds.length; i++) {
if (wm.getWindowTitle(hwnds[i]) != null && wm.getWindowTitle(hwnds[i]).endsWith("Edge")) {
wm.moveWindow(hwnds[i], 0, 0);
Thread.sleep(10);
wm.bringWindowFront(hwnds[i]);
Thread.sleep(10);
wm.resizeWindow(hwnds[i], 1424, 1039);
Thread.sleep(10);
wm.bringWindowFront(hwnds[i]);
Thread.sleep(1000);
break;
}
}
robot.click(1220, 64);
Thread.sleep(1000);
robot.click(990, 160);
//启动WSA的via浏览器.
robot.run("\"C:\\Users\\Denvo\\AppData\\Local\\Microsoft\\WindowsApps\\MicrosoftCorporationII.WindowsSubsystemForAndroid_8wekyb3d8bbwe\\WsaClient.exe\" /launch wsa://mark.via", null);
//等待1分钟进行加载.
Thread.sleep(60000);
hwnds = wm.getAllWindow();
for (int i = 0; i < hwnds.length; i++) {
if (wm.getWindowTitle(hwnds[i]) != null && wm.getWindowTitle(hwnds[i]).endsWith("Via")) {
wm.moveWindow(hwnds[i], 1427, 6);
Thread.sleep(10);
wm.bringWindowFront(hwnds[i]);
Thread.sleep(10);
wm.resizeWindow(hwnds[i], 485, 1018);
Thread.sleep(10);
wm.bringWindowFront(hwnds[i]);
Thread.sleep(1000);
break;
}
}
robot.click(1468, 70);
Thread.sleep(1000);
robot.click(1477, 322);
Thread.sleep(1000);
robot.click(1550, 493);
Thread.sleep(1000);
robot.click(1515, 695);
//等待一定时间后关闭窗口并关机.
Thread.sleep(1800000);
robot.click(1041, 22);
Thread.sleep(1000);
robot.click(1889, 29);
robot.run("shutdown -s -t 0", null);
}
}
打包jar文件并放到目标机器
我使用了Maven,我在Maven中这么配置pom.xml:(抱歉因为一些原因没有转成代码块)
通过Maven的package打包项目成jar文件后,直接把这个jar文件扔到我那台i3-3220的机器上,然后编写一个启动文件(批处理文件):
java -jar ./AutoRunner-0.0.1.jar
将这个启动文件放在与jar文件同目录下,然后创建这个启动文件的快捷方式,并将快捷方式放在C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
目录下实现开机启动。
或者,使用我之前写的自动化运行批处理文件的程序(发布地址,下载,用法),以免启动批处理文件的黑框框。
附加选项
可以给电脑安装个ToDesk啊,向日葵啊,甚至Sunshine也行,到时候就可以通过远程查看这个系统运行是否正常。当然,不安装也是可以的。
结束
现在,重启我的i3-3220电脑,这套自动化Bing Rewards系统就启动了,我把这个挂一段时间,就可以兑换很多东西了,需求解决,nice!