【发布时间】:2011-01-20 17:22:18
【问题描述】:
有没有一种方法可以使用 Java(或 Groovy)在 Windows XP 中更改我的桌面墙纸?我有一个程序每天(或随时)创建一个新图像,我想要一种自动更新我的桌面的方法。
我在这个网站上似乎有一些关于 C++ 或 .NET 的问题,但我没有看到任何特定于 Java 的内容。
【问题讨论】:
有没有一种方法可以使用 Java(或 Groovy)在 Windows XP 中更改我的桌面墙纸?我有一个程序每天(或随时)创建一个新图像,我想要一种自动更新我的桌面的方法。
我在这个网站上似乎有一些关于 C++ 或 .NET 的问题,但我没有看到任何特定于 Java 的内容。
【问题讨论】:
这是一个纯 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"));
}
}
【讨论】:
对不起,我有点落后于@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 大概是稳定的。
我之前省略了选项SPIF_UPDATEINIFILE 和SPIF_SENDWININICHANGE。这些现在正在按照 Coding4Fun MSDN 文章中的建议使用。
【讨论】:
The method loadLibrary(String, Class<WallpaperChanger.SPI>, new HashMap<Object,Object>(){}) is undefined for the type Native 这是使用的 JNA 版本,如果早于 4.1.0,我在哪里可以找到此下载?
new HashMap<Object, Object>() 更改为 new HashMap<String, Object>。此外,它不适合我的形象。认为它适用于提到的默认图像。
你可以更轻松:
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);
}
}
【讨论】:
您可以编写一个batch file to change the wall-paper,然后使用该批处理文件,
Runtime.getRuntime.exec()
【讨论】:
JNA java 库允许您轻松调用 Win32 API 调用。特别是要改变桌面背景,需要调用SystemParametersInfo 函数。
查看这篇文章了解 JNA 的介绍:http://today.java.net/article/2009/11/11/simplify-native-code-access-jna
【讨论】: