开发者

Can I change my Windows desktop wallpaper programmatically in Java/Groovy?

Is there a way I can use Java (or Groovy) to change my desktop wallpaper in Windows XP? I have a program开发者_JAVA百科 that creates a new image every day (or whenever) and I would like a way to automatically update my desktop.

I've seem some questions on this site about C++ or .NET, but I did not see anything specific to Java.


Sorry I'm a bit behind @ataylor's answer because I was preparing a snippet to do it. Yes, JNA is a correct approach. Here you go:

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
        );
  }
}

You need to have the JNA libraries on the classpath for this to work. This was tested in Windows 7, there might be some nuances in XP but I think it should work. That API is presumably stable.

References

  • Setting Wallpaper - Coding4Fun
  • How to determine if a screensaver is running in Java?
  • W32API.java

Edit (2010/01/20):

I had previously omitted the options SPIF_UPDATEINIFILE and SPIF_SENDWININICHANGE. These are now being used as they were suggested in the Coding4Fun MSDN article.


You can do it easier:

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);
   }
 }


You can write a batch file to change the wall-paper, and execute that batch file using,

Runtime.getRuntime.exec()


The JNA java library allows you to easily call Win32 API calls. In particular, to change the desktop background, you need to call the SystemParametersInfo function.

Take a look at this article for an introduction to JNA: http://today.java.net/article/2009/11/11/simplify-native-code-access-jna


Here is a Pure Java implementation which uses Project Panama to make the native callbacks into Windows USER32.DLL. Note that the API is incubating so has changed between JDK16, 17 and later builds. These samples use the versions of Panama that are in the current JDK16/17 release, some changes may be required if you switch to the latest Panama Early Access builds.

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"));
        }
    }
}

Small changes needed for 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"));
    }
}


Expanding on the answer from @DuncG, here's an updated solution which uses Project Panama from JDK 18.

import jdk.incubator.foreign.*;

import java.lang.invoke.MethodHandle;
import java.nio.file.Path;

import static jdk.incubator.foreign.ValueLayout.*;

public class WindowsOperatingSystem {
    private static final int SPI_SETDESKWALLPAPER  = 0x0014;
    private static final int SPIF_UPDATEINIFILE    = 0x01;
    private static final int SPIF_SENDCHANGE       = 0x02;

    private static final MethodHandle systemParametersInfoAFunction;

    static {
        System.loadLibrary("user32");
        // BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
        systemParametersInfoAFunction = CLinker.systemCLinker().downcallHandle(
                SymbolLookup.loaderLookup().lookup("SystemParametersInfoA").get(),
                FunctionDescriptor.of(JAVA_BOOLEAN, JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT)
        );
    }

    public static void setWallpaper(Path file) {
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {
            SegmentAllocator allocator = SegmentAllocator.nativeAllocator(scope);
            Addressable nativeFilePath = allocator.allocateUtf8String(file.toString());
            var result = (boolean)systemParametersInfoAFunction.invokeExact(
                    SPI_SETDESKWALLPAPER,
                    0,
                    nativeFilePath,
                    SPIF_UPDATEINIFILE | SPIF_SENDCHANGE
            );
            if (!result) {
                throw new IllegalStateException();
            }
        } catch (Error | RuntimeException t) {
            throw t;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜