【问题标题】:How do you keep the machine awake?如何让机器保持清醒?
【发布时间】:2010-09-08 08:21:05
【问题描述】:

我有一个用 Java 编写的服务器式软件,可以在 Windows 和 OS X 上运行。(它不是在服务器上运行,而只是在普通用户的 PC 上运行 - 类似于 torrent 客户端。)我想要软件向操作系统发出信号以在机器处于活动状态时保持机器唤醒(防止其进入睡眠模式)。

当然,我不希望有一个跨平台的解决方案,但我希望有一些非常小的 C 程序/脚本,我的应用可以生成这些 C 程序/脚本来通知操作系统保持清醒。

有什么想法吗?

【问题讨论】:

    标签: java windows macos cross-platform operating-system


    【解决方案1】:

    我使用此代码来防止我的工作站被锁定。它目前只设置为每分钟移动一次鼠标,但您可以轻松调整它。

    这是一个 hack,而不是一个优雅的解决方案。

    import java.awt.*;
    import java.util.*;
    public class Hal{
    
        public static void main(String[] args) throws Exception{
            Robot hal = new Robot();
            Random random = new Random();
            while(true){
                hal.delay(1000 * 60);
                int x = random.nextInt() % 640;
                int y = random.nextInt() % 480;
                hal.mouseMove(x,y);
            }
        }
    }
    

    【讨论】:

    • 为什么不像@Keng 提到的那样将鼠标移动 1 个像素?这似乎会让用户发疯。
    • 你可以这样做,但我不需要。我刚刚发布了我正在使用的代码。我花了大约 2 分钟来写,我不需要更改它。如果您想发布修改后的版本以将其移动一个像素,那会很酷。
    • 几年前我刚做了一个类似的“鼠标移动器”:simu.wikidot.com/java:java#toc0 :-)
    • 如果安装了 Java 的无头版本(仅限 Linux),这将不起作用。
    • 也可以使用Toolkit.getDefaultToolkit().getScreenSize()获取屏幕大小,使用screen.widthscreen.height代替640480
    【解决方案2】:

    在 Windows 上,使用 SystemParametersInfo 函数。这是一种瑞士军队风格的功能,可让您获取/设置各种系统设置。

    禁用屏幕关闭,例如:

    SystemParametersInfo( SPI_SETPOWEROFFACTIVE, 0, NULL, 0 );
    

    完成后请务必将其重新设置...

    【讨论】:

    • 非常糟糕的主意。如果程序崩溃,那么您只是未经许可更改了用户设置。您应该改为处理 WM_POWERBROADCAST 消息。
    • 这绝对是使用这种方法的危险;这可以通过创建一个在其构造函数中进行此调用并在析构函数中重置它的类来稍微缓解(在 C++ 中)。处理 WM_POWERBROADCAST 事件也可能有效,但仅限于某些风格的 Windows。
    • 你不需要走那条路。您可以通过调用PowerCreateRequest 创建电源请求。然后,生成的句柄允许您通过调用 PowerSetRequest 增加请求数或通过调用 PowerClearRequest 减少请求数。如果请求数不为零,则操作系统不会进入睡眠状态。这都记录在 Win32API 文档中。另一个优点是,如果在 JVM 关闭期间句柄被破坏,那么电源请求将被删除。底部列出的JNA solution 遵循这种方法。
    【解决方案3】:

    在上面的scarcher2 的代码中添加sn-p 并将鼠标仅移动1 个像素。我已经移动了两次鼠标,因此即使指针处于极端位置也会发生一些变化:

    while(true){
                hal.delay(1000 * 30);       
                Point pObj = MouseInfo.getPointerInfo().getLocation();
                System.out.println(pObj.toString() + "x>>" + pObj.x + "  y>>" + pObj.y);
                hal.mouseMove(pObj.x + 1, pObj.y + 1);  
                hal.mouseMove(pObj.x - 1, pObj.y - 1);
                pObj = MouseInfo.getPointerInfo().getLocation();
                System.out.println(pObj.toString() + "x>>" + pObj.x + "  y>>" + pObj.y);
            }
    

    【讨论】:

    • 你实际上可以将它移动到它所在的位置hal.mouseMove(pObj.x, pObj.y);
    【解决方案4】:

    我有一种非常强力的技术,将鼠标沿 x 方向移动 1 点,然后每 3 分钟返回一次。

    我可能有一个更优雅的解决方案,但它是一个快速解决方案。

    【讨论】:

      【解决方案5】:

      所有来回移动鼠标的建议不会让用户发疯吗?我知道我会尽快删除任何会这样做的应用程序。

      【讨论】:

      • 将它移动一个像素对任何人来说都不会引起注意,但电子人和电子人无论如何都会感到沮丧,所以你们一切都好。
      • Jip,更好的方法是使用 JNA。另外,移动鼠标会阻止屏幕关闭,而使用 JNA 解决方案时屏幕会关闭,但会阻止操作系统进入睡眠状态。
      【解决方案6】:

      更简洁的解决方案是使用 JNA 来利用本机 OS API。在运行时检查您的平台,如果它恰好是 Windows,那么以下将起作用:

      import com.sun.jna.Native;
      import com.sun.jna.Structure;
      import com.sun.jna.Structure.FieldOrder;
      import com.sun.jna.platform.win32.WTypes.LPWSTR;
      import com.sun.jna.platform.win32.WinBase;
      import com.sun.jna.platform.win32.WinDef.DWORD;
      import com.sun.jna.platform.win32.WinDef.ULONG;
      import com.sun.jna.platform.win32.WinNT.HANDLE;
      import com.sun.jna.win32.StdCallLibrary;
      
      /**
       * Power management.
       *
       * @see <a href="https://stackoverflow.com/a/20996135/14731">https://stackoverflow.com/a/20996135/14731</a>
       */
      public enum PowerManagement
      {
          INSTANCE;
      
          @FieldOrder({"version", "flags", "simpleReasonString"})
          public static class REASON_CONTEXT extends Structure
          {
              public static class ByReference extends REASON_CONTEXT implements Structure.ByReference
              {
              }
      
              public ULONG version;
              public DWORD flags;
              public LPWSTR simpleReasonString;
          }
      
          private interface Kernel32 extends StdCallLibrary
          {
              HANDLE PowerCreateRequest(REASON_CONTEXT.ByReference context);
      
              /**
               * @param powerRequestHandle the handle returned by {@link #PowerCreateRequest(REASON_CONTEXT.ByReference)}
               * @param requestType        requestType is the ordinal value of {@link PowerRequestType}
               * @return true on success
               */
              boolean PowerSetRequest(HANDLE powerRequestHandle, int requestType);
      
              /**
               * @param powerRequestHandle the handle returned by {@link #PowerCreateRequest(REASON_CONTEXT.ByReference)}
               * @param requestType        requestType is the ordinal value of {@link PowerRequestType}
               * @return true on success
               */
              boolean PowerClearRequest(HANDLE powerRequestHandle, int requestType);
      
              enum PowerRequestType
              {
                  PowerRequestDisplayRequired,
                  PowerRequestSystemRequired,
                  PowerRequestAwayModeRequired,
                  PowerRequestMaximum
              }
          }
      
          private final Kernel32 kernel32;
          private HANDLE handle = null;
      
          PowerManagement()
          {
              // Found in winnt.h
              ULONG POWER_REQUEST_CONTEXT_VERSION = new ULONG(0);
              DWORD POWER_REQUEST_CONTEXT_SIMPLE_STRING = new DWORD(0x1);
      
              kernel32 = Native.load("kernel32", Kernel32.class);
              REASON_CONTEXT.ByReference context = new REASON_CONTEXT.ByReference();
              context.version = POWER_REQUEST_CONTEXT_VERSION;
              context.flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
              context.simpleReasonString = new LPWSTR("Your reason for changing the power setting");
              handle = kernel32.PowerCreateRequest(context);
              if (handle == WinBase.INVALID_HANDLE_VALUE)
                  throw new AssertionError(Native.getLastError());
          }
      
          /**
           * Prevent the computer from going to sleep while the application is running.
           */
          public void preventSleep()
          {
              if (!kernel32.PowerSetRequest(handle, Kernel32.PowerRequestType.PowerRequestSystemRequired.ordinal()))
                  throw new AssertionError("PowerSetRequest() failed");
          }
      
          /**
           * Allow the computer to go to sleep.
           */
          public void allowSleep()
          {
              if (!kernel32.PowerClearRequest(handle, Kernel32.PowerRequestType.PowerRequestSystemRequired.ordinal()))
                  throw new AssertionError("PowerClearRequest() failed");
          }
      }
      

      然后当用户运行powercfg /requests 他们看到:

      SYSTEM:
      [PROCESS] \Device\HarddiskVolume1\Users\Gili\.jdks\openjdk-15.0.2\bin\java.exe
      Your reason for changing the power setting
      

      你应该能够为 macOS 和 Linux 做类似的事情。

      【讨论】:

      • 疯了,我不敢相信这不是投票最多的答案,因为这显然是正确的方法,而且比干扰用户的鼠标位置要优雅得多。此外,这种方法的好处是允许屏幕关闭,同时防止操作系统进入睡眠状态。
      • 我注意到一件事,PowerSetRequest 方法增加了一个计数器。因此,如果您两次调用该函数,则需要两次相应的PowerClearRequest 调用以再次将计数器减为 0 以允许操作系统进入睡眠状态。我通过保留一个布尔标志来防止这种情况发生,该标志表明我已经调用了PowerSetRequest。另外,我有一个清理方法来释放手柄。我通过调用kernel32.CloseHandle(handle) 来做到这一点。这需要我向Kernel32 接口添加一个boolean CloseHandle(HANDLE handle) 方法。
      【解决方案7】:

      这里完成了生成java代码的批处理文件,编译,清理生成的文件,后台运行..(笔记本需要jdk)

      只需将其保存为 Bat 文件并运行即可。 (somefilename.bat) ;)

      @echo off
      setlocal
      
      rem rem if JAVA is set and run from :startapp labeled section below, else the program exit through :end labeled section.
      if not "[%JAVA_HOME%]"=="[]" goto start_app
      echo. JAVA_HOME not set. Application will not run!
      goto end
      
      
      :start_app
      echo. Using java in %JAVA_HOME%
      rem writes below code to Energy.java file.
      @echo import java.awt.MouseInfo; > Energy.java
      @echo import java.awt.Point; >> Energy.java
      @echo import java.awt.Robot; >> Energy.java
      @echo //Mouse Movement Simulation >> Energy.java
      @echo public class Energy { >> Energy.java
      @echo     public static void main(String[] args) throws Exception { >> Energy.java
      @echo         Robot energy = new Robot(); >> Energy.java
      @echo         while (true) { >> Energy.java
      @echo             energy.delay(1000 * 60); >> Energy.java
      @echo             Point pObj = MouseInfo.getPointerInfo().getLocation(); >> Energy.java
      @echo             Point pObj2 = pObj;  >> Energy.java
      @echo             System.out.println(pObj.toString() + "x>>" + pObj.x + "  y>>" + pObj.y); >> Energy.java
      @echo             energy.mouseMove(pObj.x + 10, pObj.y + 10); >> Energy.java
      @echo             energy.mouseMove(pObj.x - 10, pObj.y - 10); >> Energy.java
      @echo             energy.mouseMove(pObj2.x, pObj.y); >> Energy.java
      @echo             pObj = MouseInfo.getPointerInfo().getLocation(); >> Energy.java
      @echo             System.out.println(pObj.toString() + "x>>" + pObj.x + "  y>>" + pObj.y); >> Energy.java
      @echo         } >> Energy.java
      @echo     } >> Energy.java
      @echo } >> Energy.java
      
      rem compile java code.
      javac Energy.java
      rem run java application in background.
      start javaw Energy
      echo. Your Secret Energy program is running...
      goto end
      
      :end
      rem clean if files are created.
      pause
      del "Energy.class"
      del "Energy.java"
      

      【讨论】:

        【解决方案8】:

        我一直在使用pmset 来控制我的 Mac 上的睡眠模式,而且它很容易集成。这是一个粗略的示例,说明如何从 Java 调用该程序以禁用/启用睡眠模式。请注意,您需要 root 权限才能运行 pmset,因此您需要它们才能运行此程序。

        import java.io.BufferedInputStream;
        import java.io.IOException;
        
        /**
         * Disable sleep mode (record current setting beforehand), and re-enable sleep
         * mode. Works with Mac OS X using the "pmset" command.
         */
        public class SleepSwitch {
        
            private int sleepTime = -1;
        
            public void disableSleep() throws IOException {
                if (sleepTime != -1) {
                    // sleep time is already recorded, assume sleep is disabled
                    return;
                }
        
                // query pmset for the current setting
                Process proc = Runtime.getRuntime().exec("pmset -g");
                BufferedInputStream is = new BufferedInputStream(proc.getInputStream());
                StringBuffer output = new StringBuffer();
                int c;
                while ((c = is.read()) != -1) {
                    output.append((char) c);
                }
                is.close();
        
                // parse the current setting and store the sleep time
                String outString = output.toString();
                String setting = outString.substring(outString.indexOf(" sleep\t")).trim();
                setting = setting.substring(7, setting.indexOf(" ")).trim();
                sleepTime = Integer.parseInt(setting);
        
                // set the sleep time to zero (disable sleep)
                Runtime.getRuntime().exec("pmset sleep 0");
            }
        
            public void enableSleep() throws IOException {
                if (sleepTime == -1) {
                    // sleep time is not recorded, assume sleep is enabled
                    return;
                }
        
                // set the sleep time to the previously stored value
                Runtime.getRuntime().exec("pmset sleep " + sleepTime);
        
                // reset the stored sleep time
                sleepTime = -1;
            }
        }
        

        【讨论】:

        • 很好的答案,但在这种情况下,我不敢要求提升。如果每个应用都要求特权,那么安全的意义何在?
        • 没错,为小任务授予权限很不方便。但是如果将 pmset 包装在 suid 脚本中,则可以解决问题(脚本可以以 root 身份执行而无需提示)。当然,suid 脚本可能是潜在的安全漏洞……
        【解决方案9】:

        您可以使用程序 Caffeine caffiene 让您的工作站保持清醒。您可以通过 os X 中的 open 命令运行程序。

        【讨论】:

          【解决方案10】:

          在 OS X 上,只需生成 caffeinate。这将阻止系统休眠,直到 caffeinate 终止。

          【讨论】:

            【解决方案11】:

            在 Visual Studio 中创建一个简单的表单。 从工具栏中,将 Timer 控件拖到窗体上。 在初始化代码中,将计时器间隔设置为 60 秒(60000 毫秒)。 使用以下代码实现计时器回调“SendKeys.Send("{F15}");” 运行新程序。

            无需鼠标移动。

            编辑:至少在我的陆军工作站上,仅以编程方式生成鼠标和按键消息不足以让我的工作站保持登录和唤醒状态。 Java Robot 类的早期海报走在了正确的轨道上。 JAVA Robot 在操作系统的 HAL(硬件抽象层)之上或之下工作,但是我重新创建并测试了 Java/Robot 解决方案,但它没有工作 - 直到我在代码中添加了 Robot.keyPress(123)。

            【讨论】:

            • 为什么选择 Robot.keyPress(123)?
            【解决方案12】:

            在服务器上禁用电源管理不是更容易吗?有人可能会争辩说服务器不应该进入省电模式?

            【讨论】:

            • 我不认为它是服务器。我认为它在工作站上运行。
            • 正确,它是为了在用户的工作站上运行,因此我不想更改任何系统设置。但好主意。
            【解决方案13】:

            此代码将指针移动到它已经存在的相同位置,因此用户不会注意到任何差异。

            while (true) {
                Thread.sleep(180000);//this is how long before it moves
                Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
                Robot rob = new Robot();
                rob.mouseMove(mouseLoc.x, mouseLoc.y);
            }
            

            【讨论】:

              【解决方案14】:

              要使用用户 Gili 为 Windows 提供的使用 JNA 的解决方案,这里是适用于 MacOS 的 JNA 解决方案。

              一、JNA库接口:

              import com.sun.jna.Library;
              import com.sun.jna.Native;
              import com.sun.jna.platform.mac.CoreFoundation;
              import com.sun.jna.ptr.IntByReference;
              
              public interface ExampleIOKit extends Library {
                  ExampleIOKit INSTANCE = Native.load("IOKit", ExampleIOKit.class);
              
                  CoreFoundation.CFStringRef kIOPMAssertPreventUserIdleSystemSleep = CoreFoundation.CFStringRef.createCFString("PreventUserIdleSystemSleep");
                  CoreFoundation.CFStringRef kIOPMAssertPreventUserIdleDisplaySleep = CoreFoundation.CFStringRef.createCFString("PreventUserIdleDisplaySleep");
              
                  int kIOReturnSuccess = 0;
              
                  int kIOPMAssertionLevelOff = 0;
                  int kIOPMAssertionLevelOn = 255;
              
                  int IOPMAssertionCreateWithName(CoreFoundation.CFStringRef assertionType,
                                                  int assertionLevel,
                                                  CoreFoundation.CFStringRef reasonForActivity,
                                                  IntByReference assertionId);
              
                  int IOPMAssertionRelease(int assertionId);
              }
              

              以下是调用 JNA 方法来打开或关闭睡眠预防的示例:

              public class Example {
                  private static final Logger _log = LoggerFactory.getLogger(Example.class);
              
                  private int sleepPreventionAssertionId = 0;
              
                  public void updateSleepPrevention(final boolean isEnabled) {
                      if (isEnabled) {
                          if (sleepPreventionAssertionId == 0) {
                              final var assertionIdRef = new IntByReference(0);
                              final var reason = CoreFoundation.CFStringRef.createCFString(
                                  "Example preventing display sleep");
                              final int result = ExampleIOKit.INSTANCE.IOPMAssertionCreateWithName(
                                  ExampleIOKit.kIOPMAssertPreventUserIdleDisplaySleep,
                                  ExampleIOKit.kIOPMAssertionLevelOn, reason, assertionIdRef);
                              if (result == ExampleIOKit.kIOReturnSuccess) {
                                  _log.info("Display sleep prevention enabled");
                                  sleepPreventionAssertionId = assertionIdRef.getValue();
                              }
                              else {
                                  _log.error("IOPMAssertionCreateWithName returned {}", result);
                              }
                          }
                      }
                      else {
                          if (sleepPreventionAssertionId != 0) {
                              final int result = ExampleIOKit.INSTANCE.IOPMAssertionRelease(sleepPreventionAssertionId);
                              if (result == ExampleIOKit.kIOReturnSuccess) {
                                  _log.info("Display sleep prevention disabled");
                              }
                              else {
                                  _log.error("IOPMAssertionRelease returned {}", result);
                              }
                              sleepPreventionAssertionId = 0;
                          }
                      }
                  }
              }
              

              【讨论】:

                【解决方案15】:

                在计时器内运行命令,例如 ping 服务器。

                【讨论】:

                  【解决方案16】:

                  我只是做一个移动鼠标的功能(或下载免费赠品应用程序)。不优雅,但很容易。

                  【讨论】:

                    【解决方案17】:

                    这会起作用:

                    public class Utils {
                        public static void main(String[] args) throws AWTException {
                            Robot rob = new Robot();
                            PointerInfo ptr = null;
                            while (true) {
                                rob.delay(4000);  // Mouse moves every 4 seconds
                                ptr = MouseInfo.getPointerInfo();
                                rob.mouseMove((int) ptr.getLocation().getX() + 1, (int) ptr.getLocation().getY() + 1);
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案18】:

                      我用来避免“Windows 桌面自动锁定”的一种简单方法是每 6 秒“打开/关闭 NumLock”。

                      这里是一个打开/关闭 NumLock 的 Java 程序。

                      import java.util.*;
                      import java.awt.*;
                      import java.awt.event.*;
                      
                      public class NumLock extends Thread {
                        public void run() {
                          try {
                          boolean flag = true;
                          do {
                              flag = !flag;
                      
                              Thread.sleep(6000);
                              Toolkit.getDefaultToolkit().setLockingKeyState(KeyEvent. VK_NUM_LOCK, flag);
                          }
                          while(true);
                          }
                          catch(Exception e) {}
                        }
                      
                        public static void main(String[] args) throws Exception {
                          new NumLock().start();
                        }
                      }
                      

                      在单独的命令提示符下运行这个 Java 程序; :-)

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2020-04-16
                        • 1970-01-01
                        • 2014-01-08
                        • 2014-03-29
                        • 2019-08-27
                        • 1970-01-01
                        • 2014-01-23
                        • 1970-01-01
                        相关资源
                        最近更新 更多