【问题标题】:Can I change my Windows desktop wallpaper programmatically in Java/Groovy?我可以在 Java/Groovy 中以编程方式更改我的 Windows 桌面壁纸吗?
【发布时间】:2011-01-20 17:22:18
【问题描述】:

有没有一种方法可以使用 Java(或 Groovy)在 Windows XP 中更改我的桌面墙纸?我有一个程序每天(或随时)创建一个新图像,我想要一种自动更新我的桌面的方法。

我在这个网站上似乎有一些关于 C++ 或 .NET 的问题,但我没有看到任何特定于 Java 的内容。

【问题讨论】:

    标签: java groovy


    【解决方案1】:

    这是一个纯 Java 实现,它使用 Project Panama 将本机回调制作成 Windows USER32.DLL。请注意,API 正在孵化,因此在 JDK16、17 和更高版本之间发生了变化。这些示例使用当前 JDK16/17 版本中的 Panama 版本,如果您切换到最新的 Panama Early Access 版本,可能需要进行一些更改。

    import java.lang.invoke.*;
    import java.nio.file.Path;
    import jdk.incubator.foreign.*;
    
    /**
     %JDK16%\bin\java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign SetWallpaper.java A.JPG
     */
    public class SetWallpaper {
        static final int SPI_SETDESKWALLPAPER  = 0x0014;
        static final int SPIF_UPDATEINIFILE    = 0x01;
        static final int SPIF_SENDCHANGE       = 0x02;
        public static void main(String[] args) throws Throwable {
            LibraryLookup user32 = LibraryLookup.ofLibrary("user32");
            MethodHandle spi = CLinker.getInstance().downcallHandle(user32.lookup("SystemParametersInfoA").get()
                // BOOL SystemParametersInfoA         (UINT uiAction,  UINT uiParam,   PVOID pvParam,       UINT fWinIni);
                , MethodType.methodType(int.class,     int.class,      int.class,      MemoryAddress.class, int.class)
                , FunctionDescriptor.of(CLinker.C_LONG,CLinker.C_LONG, CLinker.C_LONG, CLinker.C_POINTER,   CLinker.C_LONG));
        
            Path path = Path.of(args[0]).toRealPath();
        
            try (NativeScope scope = NativeScope.unboundedScope()) {
                MemorySegment img = CLinker.toCString(path.toString(), scope);
                int status = (int)spi.invokeExact(SPI_SETDESKWALLPAPER, 0, img.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
                System.out.println("Changed wallpaper to "+path+" rc="+status+(status == 0 ? " *** ERROR ***": " OK"));
            }
        }
    }
    

    JDK17 需要的小改动:

    /**
    %JAVA_HOME%\bin\java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign SetWallpaper.java A.JPG
    */
    public static void main(String[] args) throws Throwable {
        System.loadLibrary("user32");
        // BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
        MemoryAddress symbol = SymbolLookup.loaderLookup().lookup("SystemParametersInfoW").get();
        MethodHandle SystemParametersInfoW = CLinker.getInstance().downcallHandle(symbol
            , MethodType.methodType(int.class,     int.class,      int.class,      MemoryAddress.class, int.class)
            , FunctionDescriptor.of(CLinker.C_LONG,CLinker.C_LONG, CLinker.C_LONG, CLinker.C_POINTER,   CLinker.C_LONG));
    
        Path path = Path.of(args[0]).toRealPath();
    
        try(ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
            // toCString as WIDE string
            Addressable wide = allocator.allocateArray(CLinker.C_CHAR, (path+"\0").getBytes(StandardCharsets.UTF_16LE));
            int status = (int)SystemParametersInfoW.invokeExact(SPI_SETDESKWALLPAPER, 0, wide.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
            System.out.println("Changed wallpaper to "+path+" rc="+status+(status == 0 ? " *** ERROR ***": " OK"));
        }
    }
    

    【讨论】:

      【解决方案2】:

      对不起,我有点落后于@ataylor 的回答,因为我正在准备一个 sn-p 来做这件事。是的,JNA 是一个正确的方法。给你:

      import java.util.HashMap;
      
      import com.sun.jna.Native;
      import com.sun.jna.platform.win32.WinDef.UINT_PTR;
      import com.sun.jna.win32.*;
      
      public class WallpaperChanger {
         public static void main(String[] args) {
            //supply your own path instead of using this one
            String path = "C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg";
      
            SPI.INSTANCE.SystemParametersInfo(
                new UINT_PTR(SPI.SPI_SETDESKWALLPAPER), 
                new UINT_PTR(0), 
                path, 
                new UINT_PTR(SPI.SPIF_UPDATEINIFILE | SPI.SPIF_SENDWININICHANGE));
         }
      
         public interface SPI extends StdCallLibrary {
      
            //from MSDN article
            long SPI_SETDESKWALLPAPER = 20;
            long SPIF_UPDATEINIFILE = 0x01;
            long SPIF_SENDWININICHANGE = 0x02;
      
            SPI INSTANCE = (SPI) Native.loadLibrary("user32", SPI.class, new HashMap<Object, Object>() {
               {
                  put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
                  put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
               }
            });
      
            boolean SystemParametersInfo(
                UINT_PTR uiAction,
                UINT_PTR uiParam,
                String pvParam,
                UINT_PTR fWinIni
              );
        }
      }
      

      您需要在类路径中包含 JNA 库才能使其工作。这是在 Windows 7 中测试的,在 XP 中可能存在一些细微差别,但我认为它应该可以工作。该 API 大概是稳定的。

      参考

      编辑(2010/01/20):

      我之前省略了选项SPIF_UPDATEINIFILESPIF_SENDWININICHANGE。这些现在正在按照 Coding4Fun MSDN 文章中的建议使用。

      【讨论】:

      • 这很奇怪——我只是无法用这个(或任何其他方式)在 Java 中设置壁纸工作。它看起来与 MSDN 示例完全一致。有趣的是,UINT_PTR 导入不在我拥有的 JNA 版本中。
      • 编辑:我的问题是在设置为墙纸之前没有关闭输出文件!
      • @GregReynolds 除了 jna.jar 之外,您还需要下载 platform.jar。那将有 UINT_PTR。您可以找到here 的最新版本。
      • 我知道这是旧的,但我收到一个错误:The method loadLibrary(String, Class&lt;WallpaperChanger.SPI&gt;, new HashMap&lt;Object,Object&gt;(){}) is undefined for the type Native 这是使用的 JNA 版本,如果早于 4.1.0,我在哪里可以找到此下载?
      • @Scruffy,将 HashMap 类型从 new HashMap&lt;Object, Object&gt;() 更改为 new HashMap&lt;String, Object&gt;。此外,它不适合我的形象。认为它适用于提到的默认图像。
      【解决方案3】:

      你可以更轻松:

      import com.sun.jna.Library;
      import com.sun.jna.Native;
      import com.sun.jna.platform.win32.WinDef.HWND;
      import com.sun.jna.platform.win32.WinDef.PVOID;
      import com.sun.jna.win32.W32APIOptions;
      public class Wallpaper {    
       public static interface User32 extends Library {
           User32 INSTANCE = (User32) Native.loadLibrary("user32",User32.class,W32APIOptions.DEFAULT_OPTIONS);        
           boolean SystemParametersInfo (int one, int two, String s ,int three);         
       }
      public static void main(String[] args) {   
         User32.INSTANCE.SystemParametersInfo(0x0014, 0, "C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg" , 1);
         }
       }
      

      【讨论】:

      • 它默认使用提到的图像。但它不适用于我的图像,即使我的文件存在于给定位置。
      【解决方案4】:

      您可以编写一个batch file to change the wall-paper,然后使用该批处理文件,

      Runtime.getRuntime.exec()

      【讨论】:

      • 这里是一个可以设置桌面壁纸的C#命令行程序。作者最初创建它是为了从用... Java 编写的程序调用它。 sg20.com/wallpaperchanger
      • @Alex:感谢发帖。这对我来说非常有效,即使没有一个 JNA 示例对我有用(尽管没有出现错误,也没有进行壁纸更改......)。
      • 这基本上是仅链接的答案。如果链接中断,您可以在此答案中添加批处理吗?
      【解决方案5】:

      JNA java 库允许您轻松调用 Win32 API 调用。特别是要改变桌面背景,需要调用SystemParametersInfo 函数。

      查看这篇文章了解 JNA 的介绍:http://today.java.net/article/2009/11/11/simplify-native-code-access-jna

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        • 2019-09-30
        • 2017-12-25
        • 2017-03-03
        • 1970-01-01
        • 1970-01-01
        • 2011-03-20
        相关资源
        最近更新 更多