Могу ли я программно изменить обои на рабочем столе Windows в Java/Groovy?

Можно ли использовать Java (или Groovy) для изменения обоев рабочего стола в Windows XP? У меня есть программа, которая создает новое изображение каждый день (или когда-либо), и я хотел бы, чтобы я автоматически обновлял свой рабочий стол.

Мне кажется, на этом сайте есть несколько вопросов о C++ или.NET, но я не вижу ничего специфического для Java.

6 ответов

Решение

Извините, я немного за ответ @ataylor, потому что я готовил фрагмент для этого. Да, 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_UPDATEINIFILE а также SPIF_SENDWININICHANGE, Теперь они используются так, как они были предложены в статье Coding4Fun MSDN.

Вы можете сделать это проще:

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

Вы можете написать пакетный файл, чтобы изменить обои, и выполнить этот пакетный файл, используя,

Runtime.getRuntime.exec()

Java-библиотека JNA позволяет легко вызывать вызовы Win32 API. В частности, чтобы изменить фон рабочего стола, вам нужно вызвать функцию SystemParametersInfo.

Взгляните на эту статью для ознакомления с JNA: http://today.java.net/article/2009/11/11/simplify-native-code-access-jna

Вот реализация Pure Java, которая использует JDK16 Project Panama для выполнения собственных обратных вызовов в Windows USER32.DLL. Для запуска требуется версия JDK16, но не требуется сборка 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 -cp your.jar SetWallpaper A.JPG
 */
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));

    final int SPI_SETDESKWALLPAPER  = 0x0014;
    final int SPIF_UPDATEINIFILE    = 0x01;
    final int SPIF_SENDCHANGE       = 0x02;

    Path path = Path.of(args[0]).toAbsolutePath();

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

Расширяя ответ @DuncG, вот обновленное решение, в котором используется Project Panama из 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);
        }
    }
}
Другие вопросы по тегам