17

Is there a way I can use Java (or Groovy) to change my desktop wallpaper in Windows XP? I have a program 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.

Matt N
  • 1,086
  • 2
  • 9
  • 21
  • Related: [How to **get** the path of current Windows wallpaper](https://stackoverflow.com/a/74432415/8583692) – Mahozad Nov 14 '22 at 13:34

6 Answers6

29

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

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.

Community
  • 1
  • 1
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • It's very strange - I just could not get this (or any other way) to set the wallpaper in Java to work. It looks exactly in line with the MSDN example. Interestingly, the UINT_PTR import isn't in the JNA version I have. – Greg Reynolds Feb 18 '11 at 11:16
  • 1
    Edit: my problem was not closing the output file before setting it as wallpaper! – Greg Reynolds Feb 18 '11 at 11:43
  • @GregReynolds You need to download the platform.jar in addition to jna.jar. That will have UINT_PTR. You can find the newest versions of both [here](https://github.com/twall/jna#readme). – Duncan Calvert Feb 20 '13 at 07:00
  • 1
    I know that this is old, but I'm getting an error: `The method loadLibrary(String, Class, new HashMap(){}) is undefined for the type Native` What JNA version is this using, and if older than 4.1.0, where can I find a download for this? – Scruffy Feb 21 '15 at 04:36
  • @Scruffy, change HashMap type from `new HashMap()` to `new HashMap`. Also, its not working for my image. Thought its working for by default image which is mentioned. – Vishwajit R. Shinde May 03 '17 at 08:52
  • @VishwajitR.Shinde I looked up my code from two years ago and had managed to make it work with `new HashMap()`, which makes sense because [the type in a generic constructor does not effect the value of the object created](https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html). – Scruffy May 04 '17 at 11:17
6

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);
   }
 }
tvkanters
  • 3,519
  • 4
  • 29
  • 45
Andrey
  • 1,528
  • 14
  • 12
3

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

Runtime.getRuntime.exec()

Eternal Noob
  • 2,717
  • 5
  • 27
  • 41
  • 1
    Here is a C# command line program that can set a desktop wallpaper. The author initially created it for calling it from a program written in ... Java. http://www.sg20.com/wallpaperchanger/ – Stephan Nov 23 '11 at 10:24
  • @Alex: Thanks for posting. That worked perfectly for me, even though none of the JNA examples worked for me (despite no errors appearing, no wallpaper changes were made...). – Josh1billion Nov 27 '13 at 22:58
  • This is basically link only answer. Can you add batch to this answer in case link will break? – Piro Aug 13 '20 at 05:32
2

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

ataylor
  • 64,891
  • 24
  • 161
  • 189
2

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"));
    }
}
DuncG
  • 12,137
  • 2
  • 21
  • 33
0

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);
        }
    }
}
Shams
  • 26
  • 3