I want to catch WM_QUERYENDSESSION messages in Java with JNA so that I can execute a shutdown method because Runtime#addShutdownHook(Thread) doesn't work on Windows [1]. I know this can be done since I've seen it implemented with JNIWrapper but I would like to have a JNA-based solution.
JNIWrapper solution
import java.io.File;
import java.io.RandomAccessFile;
import javax.swing.JFrame;
import com.jniwrapper.win32.Msg;
import com.jniwrapper.win32.ui.WindowMessage;
import com.jniwrapper.win32.ui.WindowMessageListener;
import com.jniwrapper.win32.ui.WindowProc;
import com.jniwrapper.win32.ui.Wnd;
pu开发者_C百科blic class ShutdownJNIWrapper {
public static void main(String[] args) {
final JFrame frame = new JFrame("Shutdown Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Wnd frameHandle = new Wnd(frame);
WindowProc frameWindowProc = new WindowProc(frameHandle);
frameWindowProc.addMessageListener(new WindowMessageListener() {
public boolean canHandle(WindowMessage windowMessage, boolean beforeWindowProc) {
return windowMessage.getMsg() == Msg.WM_QUERYENDSESSION && beforeWindowProc;
}
public int handle(WindowMessage windowMessage) {
doSomething();
return 0;
}
private void doSomething() {
final File file = new File("shutdown-jniwrapper.txt");
try {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.writeUTF("quit");
raf.close();
} catch (Exception e) {
}
}
});
frameWindowProc.substitute();
}
}
I tried to create my own User32 class with a WindowProc callback and a SetWindowLong method that accepts it as a parameter but I get the following exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'SetWindowLong': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:179)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:344)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:324)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.SetWindowLong(Unknown Source)
at ShutdownJNA.main(ShutdownJNA.java:34)
Here's my User32 class:
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary;
public interface MyUser32 extends StdCallLibrary {
public static final int WM_QUERYENDSESSION = 0x11;
public static int GWL_WNDPROC = -4;
interface WindowProc extends StdCallCallback {
LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
}
int SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);
int SetWindowLong(HWND hWnd, int nIndex, WindowProc dwNewLong);
};
and the class that tries to put everything together:
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
public class ShutdownJNA extends JPanel {
public static void main(String[] args) {
final JFrame frame = new JFrame("Shutdown Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
try {
HWND hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(frame));
MyUser32.WindowProc proc = new MyUser32.WindowProc() {
public LRESULT callback(HWND wnd, int msg, WPARAM param, LPARAM param2) {
if (msg == MyUser32.WM_QUERYENDSESSION) {}
return new LRESULT(0);
}
};
MyUser32 user32 = (MyUser32)Native.loadLibrary("user32", MyUser32.class); //$NON-NLS-1$
user32.SetWindowLong(hwnd, MyUser32.GWL_WNDPROC, proc);
} catch (Exception e) {
e.printStackTrace();
}
}
}
[1] The shutdown hook isn't executed when the application is launched using javaw.exe
Make sure you have the jar files for JNA in your environment. Both jna.jar and platform.jar must be there, otherwise the reference will fail. The other thing is, the dll files you use thru JNA must be in your environment (such as user32.dll) if you plan to load them.
精彩评论