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.
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);
}
}
}
Related
I am trying to run a ShellExecute function from java with JNA.
I dont have any problems running ShellExecuteA on non-unicode folders
import com.sun.jna.*;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.*;
public class Main {
public interface Shell32 extends ShellAPI, StdCallLibrary {
Shell32 INSTANCE = (Shell32)Native.loadLibrary("shell32", Shell32.class);
WinDef.HINSTANCE ShellExecuteA(WinDef.HWND hwnd,
String lpOperation,
String lpFile,
String lpParameters,
String lpDirectory,
int nShowCmd);
}
public static void main(String[] args) {
WinDef.HWND h = null;
WString st = new WString("D:");
Shell32.INSTANCE.ShellExecuteA(h, "open", st.toString(), null, null, 1);
}
}
But since I want to be able to use it on unicode folders I actually want to run ShellExecuteW instead of A version, but cant figure how. Every time I run the following code it just finishes executing without doing anything or showing any errors.
import com.sun.jna.*;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.*;
public class Main {
public interface Shell32 extends ShellAPI, StdCallLibrary {
Shell32 INSTANCE = (Shell32)Native.loadLibrary("shell32", Shell32.class);
WinDef.HINSTANCE ShellExecuteW(WinDef.HWND hwnd,
String lpOperation,
WString lpFile,
String lpParameters,
String lpDirectory,
int nShowCmd);
}
public static void main(String[] args) {
WinDef.HWND h = null;
WString st = new WString("D:\\日本語");
Shell32.INSTANCE.ShellExecuteW(h, "open", st, null, null, 1);
}
}
I guess the problem lies withing the third parameter lpFile - I tried using String, WString all the same.
Any help will be appreciated.
Thanks.
Because this is the answer I repeat #technomage's comment to the question here:
If you're going to explicitly use wide strings, you typically need to use them for all function parameters. w32 API will typically have LPTCSTR for all C strings, which means String for ascii and WString for unicode.
The working code is therefore:
import com.sun.jna.*;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.*;
public class Main {
public interface Shell32 extends ShellAPI, StdCallLibrary {
Shell32 INSTANCE = (Shell32)Native.loadLibrary("shell32", Shell32.class);
WinDef.HINSTANCE ShellExecuteW(WinDef.HWND hwnd,
WString lpOperation,
WString lpFile,
WString lpParameters,
WString lpDirectory,
int nShowCmd);
}
public static void main(String[] args) {
WinDef.HWND h = null;
WString file = new WString("D:\\日本語");
Shell32.INSTANCE.ShellExecuteW(h, new WString("open"), file, null, null, 1);
}
}
I get all needed information for x86 programms (in C:\Program Files (x86)) without any problems with below statement. But for x64 empty result for activ window process name. Is it possible detect active window process name (.exe path) for x64 processor with jna?
Eclipse Luna for x64
Windows 8 pro x64
JRE8 x64
jna 4.1.0
import static your.pack.name.EnumerateWindows.Kernel32.OpenProcess;
import static your.pack.name.EnumerateWindows.Kernel32.PROCESS_QUERY_INFORMATION;
import static your.pack.name.EnumerateWindows.Kernel32.PROCESS_VM_READ;
import static your.pack.name.EnumerateWindows.Psapi.GetModuleBaseNameW;
import static your.pack.name.EnumerateWindows.User32DLL.GetForegroundWindow;
import static your.pack.name.EnumerateWindows.User32DLL.GetWindowTextW;
import static your.pack.name.EnumerateWindows.User32DLL.GetWindowThreadProcessId;
import java.lang.management.ManagementFactory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.ptr.PointerByReference;
public class EnumerateWindows {
private static final int MAX_TITLE_LENGTH = 1024;
public static void main(String[] args) throws Exception {
char[] buffer = new char[MAX_TITLE_LENGTH * 2];
GetWindowTextW(GetForegroundWindow(), buffer, MAX_TITLE_LENGTH);
System.out.println("Active window title: " + Native.toString(buffer));
PointerByReference pointer = new PointerByReference();
GetWindowThreadProcessId(GetForegroundWindow(), pointer);
Pointer process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pointer.getValue());
GetModuleBaseNameW(process, null, buffer, MAX_TITLE_LENGTH);
System.out.println("Active window process: " + Native.toString(buffer));
System.out.println(ManagementFactory.getRuntimeMXBean().getName());
}
static class Psapi {
static {
Native.register("psapi");
}
public static native int GetModuleBaseNameW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size);
}
static class Kernel32 {
static {
Native.register("kernel32");
}
public static int PROCESS_QUERY_INFORMATION = 0x0400;
public static int PROCESS_VM_READ = 0x0010;
public static native int GetLastError();
public static native Pointer OpenProcess(int dwDesiredAccess, boolean bInheritHandle, Pointer pointer);
}
static class User32DLL {
static {
Native.register("user32");
}
public static native int GetWindowThreadProcessId(HWND hWnd, PointerByReference pref);
public static native HWND GetForegroundWindow();
public static native int GetWindowTextW(HWND hWnd, char[] lpString, int nMaxCount);
}
}
i get all information for x86 and x64 software without any problem, if i export my project to runnable jar.
Above problem affect only on, if i run the project from Eclipse
Can a package final variable be changed through reflection?
Say I have this:
public class Widget {
final int val = 23;
}
Can the val be changed through reflection if made accessible?
If so, is there any way to prevent it that without using the security manager?
YES. Try this code:
public static void main(String[] args) throws Exception {
Widget w = new Widget ();
Field m = Widget.class.getDeclaredField("val");
m.setAccessible(true);
m.set(w, 233);
System.out.println(m.get(w)); /// PRINT 233
}
Turns out, changing final members causes reflection-obtained values to differ from values returned by regular code! This is quite scary.
import java.lang.reflect.Field;
public class Test {
private static final class Widget {
private final int val = 23;
public int getVal() {
return val;
}
}
public static void main(String[] args) throws Exception {
Widget w = new Widget ();
Field m = Widget.class.getDeclaredField("val");
m.setAccessible(true);
m.set(w, 233);
Field m1 = Widget.class.getDeclaredField("val");
m1.setAccessible(true);
System.out.println(m.get(w)); /// PRINT 233
System.out.println(w.getVal()); /// PRINT 23
System.out.println(m1.get(w)); /// PRINT 233
}
}
Try this.
Widget() {
checkPerMission();
}
private void checkPerMission() {
Class self = sun.reflect.Reflection.getCallerClass(1);
Class caller = sun.reflect.Reflection.getCallerClass(3);
if (self != caller) {
throw new java.lang.IllegalAccessError();
}
}
Running into a maddening blocking error:
Exception in thread "main" java.lang.Error: Structure.getFieldOrder() on class com.luke.generator.GeneratorEngine$VERSION_INFO returns names ([BuildString, ProtocolMajorVer, ProtocolMinorVer]) which do not match declared field names ([BiuldString, ProtocolMajorVer, ProtocolMinorVer])
at com.sun.jna.Structure.getFields(Structure.java:925)
at com.sun.jna.Structure.deriveLayout(Structure.java:1058)
at com.sun.jna.Structure.calculateSize(Structure.java:982)
at com.sun.jna.Structure.calculateSize(Structure.java:949)
at com.sun.jna.Structure.allocateMemory(Structure.java:375)
at com.sun.jna.Structure.<init>(Structure.java:184)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.sun.jna.Structure.<init>(Structure.java:159)
at com.sun.jna.Structure.<init>(Structure.java:151)
at com.luke.generator.GeneratorEngine$.<init>(GeneratorEngine.java:108)
at com.luke.generator.connectionVersion(GeneratorEngine.java:297)
at com.luke.generator.Main.main(Main.java:161)
Platform: Intel, Windows 8
JRE 1.7, 32-bit (x86)
Eclipse Kepler, Default encoding UTF-8
jna-4.1.0.jar
32-bit CPP DLL - I can confirm that I am loading the library and calling functions that do not include parameters. I also tried passing WStrings and Strings, but that did not address the issue.
Source:
CPP struct:
typedef struct {
UINT32 ProtocolMajorVer;
UINT32 ProtocolMinorVer;
UI_STRING BuildString; // Build version for the application.
} VERSION_INFO;
CPP Function
DLL_EXPORTS RETURN_TYPES ConnectionVersion (VERSION_INFO &Version) {<body omitted>}
Java code:
//Interface definition
public interface UiApi extends StdCallLibrary {
UiApi INSTANCE = (UiApi) Native.loadLibrary(UiApiPath,UiApi.class);
final String PROTOCOLMAJORVERSION = new String("ProtocolMajorVer");
final String PROTOCOLMINORVERSION = new String("ProtocolMinorVer");
final String BUILDSTRING = new String("BuildString");
public static class VERSION_INFO extends Structure {
public static class ByReference extends VERSION_INFO implements Structure.ByReference {}
public static class ByValue extends VERSION_INFO implements Structure.ByValue {}
public int ProtocolMajorVer;
public int ProtocolMinorVer;
public byte[] BiuldString;
protected List getFieldOrder() {
return Arrays.asList(new String[] { "ProtocolMajorVer","ProtocolMinorVer","BuildString" });
}
}
//Connection
public int Connect(byte[] strServerName);
public int Disconnect();
public int CloseProject();
public int ConnectionVersion(VERSION_INFO result);
public int ConnectionVersion(VERSION_INFO.ByReference result);
public int ConnectionVersion(VERSION_INFO.ByValue result);
}
//Engine.connectionVersion() method
public static int connectionVersion() {
int nReturn = 0;
String str = new String("This is my build version\n");
UiApi uiapilib;
uiapilib = UiApi.INSTANCE;
try {
UiApi.VERSION_INFO.ByReference result = new UiApi.VERSION_INFO.ByReference();
result.ProtocolMajorVer = 0;
result.ProtocolMinorVer = 0;
result.BiuldString = str.getBytes();
nReturn = uiapilib.ConnectionVersion(result);
}
catch (Exception e) {
System.out.println("Error=" + e.getLocalizedMessage());
}
return nReturn;
}
//This is the code in main that results in exception
private static Engine engine;
engine = new GeneratorEngine();
engine.connectionVersion();
I must be missing something basic. Is there something in Eclipse that could possibly be causing the HashSet name comparisons in JNA's Structure.java (line 925) that would result in names not matching? From the exception thrown, these definitions look identical.
Thanks for any tips, guidance, fresh perspectives you can offer.
Check your spelling - the field is called BiuldString not BuildString, you have the i and u reversed.
I'm trying to detect the location of AppData\LocalLow work on Java with JNA on Windows 7. But the closest function available for the job is:
W32API.HRESULT SHGetFolderPath(W32API.HWND hwndOwner,int nFolder,W32API.HANDLE
hToken,W32API.DWORD dwFlags,char[] pszPath)
Here we have the Solution in C#
But in my case, JAVA + JNA, I'm wondering how I can use the LocalLow GUID with SHGetFolderPath only, or maybe I should look at the problem from a different angle (maybe JNI would be better here?)
If somebody can help on that, thanks
Cheers
EDIT:
Ok, now I added SHGetKnownFolderPath, but here, it keeps returning me strings like that "?f"
static interface Shell32 extends Library {
public static final int MAX_PATH = 260;
public static final String FOLDERID_APPDATALOW = "{A520A1A4-1780-4FF6-BD18-167343C5AF16}";
static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32",
Shell32.class, OPTIONS);
public int SHGetKnownFolderPath(Guid.GUID rfid, int dwFlags, HANDLE hToken,
char[] pszPath);
}
public static void main(String[] args) {
char[] pszPath = new char[Shell32.MAX_PATH];
Guid.GUID localLowId = Ole32Util.getGUIDFromString(Shell32.FOLDERID_APPDATALOW);
int hResult = Shell32.INSTANCE.SHGetKnownFolderPath(localLowId, 0, null, pszPath);
if (hResult == 0) {
String path = new String(pszPath);
int len = path.indexOf('\0');
path = path.substring(0, len);
System.out.println(path);
} else {
System.err.println("Error: " + hResult);
}
}
You can extend Shell32 (or create your own similar class) to get access to the SHGetKnownFolderPath API:
W32API.HRESULT SHGetKnownFolderPath(
Guid.GUID rfid,
W32API.DWORD dwFlags,
W32API.HANDLE hToken,
char[] pszPath);
This Question is Somewhat old However I managed to Solve this issue.
allow me to explain why Kyro approach does not work quite well. This is due
SHGetKnownFolderPath uses a POINTER and not a String or A character array (that why show odd output) . therefore you need to use something like this:
public int SHGetKnownFolderPath(Guid.GUID rfid, int dwFlags, HANDLE hToken, PointerByReference pszPath);
a pointer by reference...
so on this case i attach a example I used:
static interface Shell32 extends StdCallLibrary {
public static final String FOLDERID_APPDATALOW = "{A520A1A4-1780-4FF6-BD18-167343C5AF16}";
static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32",
Shell32.class, W32APIOptions.UNICODE_OPTIONS);
public int SHGetKnownFolderPath(Guid.GUID rfid, int dwFlags, HANDLE hToken,
PointerByReference pszPath);
}
public static void main(String[] args) {
Guid.GUID localLowId = Ole32Util.getGUIDFromString(Shell32.FOLDERID_APPDATALOW);
PointerByReference e= new PointerByReference();
int hResult = Shell32.INSTANCE.SHGetKnownFolderPath(localLowId, 0, null, e);
if (hResult == 0) {
char delim='\0';
char Array[]=e.getValue().getCharArray(0,255);
for (int i = 0; i < Array.length; i++) {
if(Array[i]==delim){
char temparr[]=new char[i];
System.arraycopy(Array,0,temparr,0,i);
Array=temparr;
break;
}
}
/*dont forget to release the Pointer*/
Ole32.INSTANCE.CoTaskMemFree(e.getValue());
System.out.println(Array);
} else {
System.err.println("Error: " + hResult);
}
}
private static interface Ole32 extends StdCallLibrary {
Ole32 INSTANCE = (Ole32) Native.loadLibrary(
"Ole32", Ole32.class, W32APIOptions.UNICODE_OPTIONS);
void CoTaskMemFree(Pointer pv);
}