There are three different scenarios when you can handle shutdown and logoff events. I will focus on the Windows Application because it works for console applications also and if by some reason your application imports User32 functions then the console handle will not work.
Basically you will need 2 functions:
ATOM RegisterClassEx(WNDCLASSEX *lpwcx);
RegisterClassEx create a new kind of window with a hook(that is our shutdown/logoff handler) associated with.
HWND WINAPI CreateWindowEx(
int dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,
int dwStyle, int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam
);
CreateWindowEx instantiates a new Window, this window have a event hook associated (by the registered class), this way Windows will notify the associated hook with all possible events.
Here a full working sample, just start it and logoff or shutdown the computer, after start it back, take a look at %userprofile%\shutdown-hook.log
file it must have handled these events by logging something like
...
action=proc-callback, event=22
...
code
public class Main {
/**
* <pre>
* Steps:
*
* 1. Create a WinProc (this function will handle all events)
* 2. Create a window class using the created WinProc
* 3. Create a window using the created window class
* 4. Use the WinProc to handle shutdown events
* </pre>
*/
public static void main(String[] args) {
// registering a window - https://msdn.microsoft.com/pt-br/library/windows/desktop/ms633587
// https://msdn.microsoft.com/pt-br/library/windows/desktop/ms633577
// typedef struct tagWNDCLASSEX {
// UINT cbSize;
// UINT style;
// WNDPROC lpfnWndProc;
// int cbClsExtra;
// int cbWndExtra;
// HINSTANCE hInstance;
// HICON hIcon;
// HCURSOR hCursor;
// HBRUSH hbrBackground;
// LPCTSTR lpszMenuName;
// LPCTSTR lpszClassName;
// HICON hIconSm;
// } WNDCLASSEX, *PWNDCLASSEX;
//
// ATOM WINAPI RegisterClassEx(
// _In_ const WNDCLASSEX *lpwcx
// );
final WinUser.WNDCLASSEX clazz = new WinUser.WNDCLASSEX();
clazz.lpszClassName = "MyCustomWindow";
clazz.cbSize = Native.getNativeSize(WinUser.WNDCLASSEX.class, null);
clazz.lpfnWndProc = new MyWinProc();
WinDef.ATOM classInst = User32.INSTANCE.RegisterClassEx(clazz);
System.out.printf("action=registerclass, clazz=%s, error=%d\n", classInst, Native.getLastError());
WinDef.HWND w = User32.INSTANCE.CreateWindowEx(
512, clazz.lpszClassName, "My Window",
WinUser.WS_OVERLAPPEDWINDOW, -2147483648, -2147483648, 250, 100,
null, null, null, null
);
System.out.printf("action=createWindow, w=%s, error=%d\n", w, Native.getLastError());
WinUser.MSG msg = new WinUser.MSG();
while (User32.INSTANCE.GetMessage(msg, null, 0, 0)) {
User32.INSTANCE.DispatchMessage(msg);
}
}
public interface User32 extends Library {
User32 INSTANCE = Native.loadLibrary("User32", User32.class, W32APIOptions.UNICODE_OPTIONS);
// ATOM WINAPI RegisterClassEx(
// _In_ const WNDCLASSEX *lpwcx
// );
WinDef.ATOM RegisterClassEx(WinUser.WNDCLASSEX lpwcx);
// HWND WINAPI CreateWindowEx(
// _In_ DWORD dwExStyle,
// _In_opt_ LPCTSTR lpClassName,
// _In_opt_ LPCTSTR lpWindowName,
// _In_ DWORD dwStyle,
// _In_ int x,
// _In_ int y,
// _In_ int nWidth,
// _In_ int nHeight,
// _In_opt_ HWND hWndParent,
// _In_opt_ HMENU hMenu,
// _In_opt_ HINSTANCE hInstance,
// _In_opt_ LPVOID lpParam
// );
WinDef.HWND CreateWindowEx(
int dwExStyle,
String lpClassName,
String lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
WinDef.HWND hWndParent,
WinDef.HMENU hMenu,
WinDef.HINSTANCE hInstance,
WinDef.LPVOID lpParam
);
// BOOL WINAPI GetMessage(
// _Out_ LPMSG lpMsg,
// _In_opt_ HWND hWnd,
// _In_ UINT wMsgFilterMin,
// _In_ UINT wMsgFilterMax
// );
boolean GetMessage(WinUser.MSG lpMsg, WinDef.HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);
// LRESULT WINAPI DispatchMessage(
// _In_ const MSG *lpmsg
// );
WinDef.LRESULT DispatchMessage(WinUser.MSG lpmsg);
WinDef.LRESULT DefWindowProc(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam);
}
/**
* <pre>
* All Possible events -
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927.aspx#system_defined
* https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/um/WinUser.h
* </pre>
*/
public static class MyWinProc implements WinUser.WindowProc {
private final OutputStream out;
public MyWinProc() {
try {
// this is unsafe because this file will never be closed, anyway it is just for a example
out = new FileOutputStream(new File(System.getProperty("user.home") + File.separator + "shutdown-hook.log"));
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
final String msg = String.format("action=proc-callback, event=%d %n", uMsg);
System.out.print(msg);
try {
out.write(msg.getBytes());
switch (uMsg){
case 0x0016:
out.write("shutdown".getBytes());
break;
case 0x0011:
out.write("logoff".getBytes());
break;
}
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
}
obs: Just a suggestion, depending your requirements I think that maybe can makes more sense if you just start a background thread instead and time by time do the task that you have to do because if the Windows gets the blue screen of death or the power go away or someone just plug off the power then the Windows events will not help you. Anyway background thread solution is quite more simple.
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
Executors.newSingleThreadExecutor().execute(() -> {
while(!Thread.currentThread().isInterrupted()){
System.out.println("do a background stuff");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {/*I will look at that in the while clause*/}
}
});
System.out.println("doing another stuff");
}
}
My dependencies
compile group: 'net.java.dev.jna', name: 'jna', version: '4.5.0'
compile group: 'net.java.dev.jna', name: 'jna-platform', version: '4.5.0'