hooking into system shutdown in Windows?

908 views
Skip to first unread message

Noel Grandin

unread,
Jun 13, 2012, 5:30:11 AM6/13/12
to jna-...@googlegroups.com
Hi

Has anybody successfully hooked into the Windows shutdown messages? I've been beating my head against this for a while, and I just can't seem to get anything useful.

I definitely seem to be getting normal windows messages, but I never seem to receive the critical WM_ENDSESSION message.

Thanks, Noel Grandin


public class ShutdownDetect
{
/**
* @see <a href="http://msdn.microsoft.com/en-us/library/aa376889.aspx">WM_ENDSESSION reference</a>
*/
public static final int WM_ENDSESSION = 0x16;
public static final int WM_QUERYENDSESSION = 0x11;

public interface IShutdownListener
{
void onShutdown();
}

public static class CWPSSTRUCT extends Structure
{
public LPARAM lParam;
public WPARAM wParam;
public DWORD message;
public HWND hwnd;
}

public interface WinHookProc extends WinUser.HOOKPROC
{
/**
*      callback function</a>
* @param nCode is an action parameter. anything less than zero indicates I should ignore this call.
* @param wParam Specifies whether the message was sent by the current thread. If the message was sent by the
*          current thread, it is nonzero; otherwise, it is zero
* @param hookProcStruct A pointer to a CWPSTRUCT structure that contains details about the message.
* @return If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
*/
WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, CWPSSTRUCT hookProcStruct);
}

public static final class MyHookProc implements WinHookProc
{
public IShutdownListener listener;
public WinUser.HHOOK hhook;

@Override
public LRESULT callback(int nCode, WPARAM wParam, CWPSSTRUCT hookProcStruct) {
if (nCode >= 0) {
System.out.println(hookProcStruct.message);
// tell the OS it's OK to shut down
if (hookProcStruct.message.longValue() == WM_QUERYENDSESSION) {
System.out.println("WM_QUERYENDSESSION");
return new LRESULT(1);
}
// process the actual shutting down message
if (hookProcStruct.message.longValue() == WM_ENDSESSION) {
System.out.println("WM_ENDSESSION");
listener.onShutdown();
return new LRESULT(0); // return success
}
}
// pass the callback on to the next hook in the chain
return User32.INSTANCE.CallNextHookEx(hhook, nCode, wParam, hookProcStruct.getPointer());
}
}

public static void register(JFrame frame, final IShutdownListener listener) {
Native.setCallbackExceptionHandler(new Callback.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Callback arg0, Throwable arg1) {
arg1.printStackTrace();
}
});

// get the window handle for the main window/frame
final HWND hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(frame));

// clear the error value
Native.setLastError(0);

// retrieve the threadID associated with the main window/frame
int windowThreadID = User32.INSTANCE.GetWindowThreadProcessId(hwnd, null);
if (windowThreadID == 0) {
int x = Native.getLastError();
throw new IllegalStateException(
"error calling GetWindowThreadProcessId when installing machine-shutdown handler " + x);
}

// clear the error value
Native.setLastError(0);

final MyHookProc proc = new MyHookProc();
proc.listener = listener;
proc.hhook = User32.INSTANCE.SetWindowsHookEx(12/* WH_CALLWNDPROCRET */, new MyHookProc(),
null, windowThreadID/* dwThreadID */);

// null indicates failure
if (proc.hhook == null) {
int x = Native.getLastError();
throw new IllegalStateException(
"error calling SetWindowsHookEx when installing machine-shutdown handler " + x);
}
System.out.println("Installed shutdown-detect hook procedure");
}

public static void main(String[] args) {
final JFrame frame = new JFrame("Shutdown Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

register(frame, new IShutdownListener() {
@Override
public void onShutdown() {
try {
PrintStream out = new PrintStream(new File("C:/SCC_Runtime/shutdownTest.txt"));
System.out.println("shutting down");
out.println("shutting down");
out.close();
}
catch (IOException ex) {}
System.exit(1);
}
});
}
}

Timothy Wall

unread,
Jun 13, 2012, 7:20:57 AM6/13/12
to jna-...@googlegroups.com
The VM may be getting a message before your application code does. Just an idea.

Noel Grandin

unread,
Jun 13, 2012, 7:40:30 AM6/13/12
to jna-...@googlegroups.com


On Wednesday, June 13, 2012 1:20:57 PM UTC+2, Timothy Wall wrote:
The VM may be getting a message before your application code does.  Just an idea.


Thanks. Some digging around in the bowels of the AWT C++ code indicates that this is probably the case.
Oh well. 

Noel Grandin

unread,
Jun 14, 2012, 4:35:46 AM6/14/12
to jna-...@googlegroups.com


On Wednesday, June 13, 2012 1:20:57 PM UTC+2, Timothy Wall wrote:
The VM may be getting a message before your application code does.  Just an idea.


Mr Wall, you, sir, are a genius!!

Thanks to that hint, I figured out that I need to modify this line of code
               proc.hhook = User32.INSTANCE.SetWindowsHookEx(12/* WH_CALLWNDPROCRET */, proc, 
                                null, windowThreadID/* dwThreadID */); 
to this
proc.hhook = User32.INSTANCE.SetWindowsHookEx(4/* WH_CALLWNDPROC */, proc,
null, windowThreadID/* dwThreadID */);

Note the critical first parameter. By installing the hook BEFORE the normal windows procedure, I can see the relevant events.



Muhammed Yuce

unread,
Sep 19, 2014, 4:46:37 AM9/19/14
to jna-...@googlegroups.com, noelg...@gmail.com
Your CWPSSTRUCT class is wrong. This class should be:

   public static class CWPSSTRUCT extends Structure {

       
public WinDef.LPARAM lParam;
       
public WinDef.WPARAM wParam;
       
public int message;
       
public WinDef.HWND hwnd;

       
@Override
       
protected List<?> getFieldOrder() {
           
return Arrays.asList("lParam", "wParam", "message", "hwnd");
       
}
   
}


Timothy Wall

unread,
Sep 19, 2014, 7:31:48 AM9/19/14
to jna-...@googlegroups.com, noelg...@gmail.com
Pull requests with associated tests (where possible) are welcome.
> --
> You received this message because you are subscribed to the Google Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Muhammed Yuce

unread,
Sep 19, 2014, 8:32:39 AM9/19/14
to jna-...@googlegroups.com, noelg...@gmail.com
Noel Grandin, When I run your codes, JVM crashes. Do you know the reason?
Reply all
Reply to author
Forward
0 new messages