Introduction
In this article i try explain how to get shutdown event from java application with jni in windows os beacuse in many situation application need save essential data before shutdown computer.
The JNI is designed to handle situations where you need to combine Java
applications with native code. As a two-way interface, the JNI can support two
types of native code: native libraries and native applications.
You can use the JNI to write native methods that allow Java applications tocall functions implemented in native libraries. Java applications call native methods in the same way that they call methods implemented in the Java programming language. Behind the scenes, however, native methods are implemented in another language and reside in native libraries.
Why we need to detect computer shutdown?
Computer shutdown detection is required to complete short, but very important actions, such as:
- Saving all unsaved data
- Notify distributed system components via network that the current component will be shut off
- Add record about the shutdown into the log in order to better analyze the situation
For get shutdown event the application must listen to WM_ENDSESSION.Windows sends a WM_ENDSESSION message to all programs with a Window before shutdown.
GUI applications
GUI application receives information about target events via window messages. We’re interested in the following window messages: WM_QUERYENDSESSION and WM_POWERBROADCAST. The first message (as seen from the title) appears when the process of closing user session is initiated. Shutting computer off as well as restarting it also causes user session to end, thus messages about it are delivered via the same window message. From the second message we can get information about the suspend event. We can get different types of events from wParam and lParam, transferred together with the message. Here is the part of our handler of window messages:
simple program
For do this i create a helloworld code example in java and create c code and make helloworld.dll .
1-Create a java class that declares the native method. a native method invoke our function in dll from java code.
helloworld.java
package helloworld;
public class HelloWorld {
static{
try{
System.load("YOUR absoulte PATH/HelloWorld.dll");
}
catch(Exception e){
System.out.println("dll not load");
}
}
private native void print();
private static void callback(){
System.out.println("shutdown Event");
}
public static void main(String[] args) {
new HelloWorld().print();
}
}
2-compile the HelloWorld source file with javac, resulting in the class file HelloWorld.class.
3-Use javah -jni to generate a C header file
HelloWorld.h
#ifndef HELLOWORLD_H
#define HELLOWORLD_H
#include <jni.h>
#ifndef _Included_helloworld_HelloWorld
#define _Included_helloworld_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#endif // HELLOWORLD_H
4-Write the C implementation of the native method.
The function registerWndProc creates the hidden window.
The function WndProc handles received messages.
HelloWorld.cpp
#include "helloworld_HelloWorld.h"
#include <jni.h>
#include <stdio.h>
#include <windows.h>
#include <signal.h>
#include <wtsapi32.h>
#include<istream>
using namespace std;
JNIEnv *cur_env;
bool sleep=false;
void registerWndProc();
BOOL WTSRegisterSessionNotification(HWND hwnd,DWORD dwFlags);
JNIEXPORT void JNICALL Java_helloworld_HelloWorld_print
(JNIEnv *env, jobject){
cur_env = env;
registerWndProc();
}
void calljvm() {
JavaVM *jvm;
cur_env->GetJavaVM(&jvm);
jint AttachCurrentThread = (*jvm).AttachCurrentThread((void **)(&cur_env),NULL);
jclass cls=cur_env->FindClass("helloworld/HelloWorld");
jmethodID mid=cur_env->GetStaticMethodID(cls,"callback","()V");
(*cur_env).CallStaticVoidMethod(cls, mid);
(*jvm).DetachCurrentThread();
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam,LPARAM lParam)
{
switch(msg) {
case WM_CREATE:
WTSRegisterSessionNotification( hwnd, NOTIFY_FOR_ALL_SESSIONS ) ;
break;
case WM_POWERBROADCAST:
if (wParam==PBT_APMSUSPEND){
sleep=true;
MessageBox(NULL, TEXT("sleep"),
NULL, NULL);
calljvm();
}
if(wParam==PBT_APMRESUMESUSPEND){
MessageBox(NULL,TEXT("wake up"),
NULL, NULL);
calljvm();
}
break;
case WM_ENDSESSION:
MessageBox(NULL,TEXT("shut down"),
NULL, NULL);
calljvm();
break;
case WM_WTSSESSION_CHANGE:
switch(wParam){
case WTS_SESSION_LOCK:
if(sleep==false){
MessageBox(NULL, TEXT("lock"),
NULL, NULL);
calljvm();
}
break;
case WTS_SESSION_UNLOCK:
if(sleep==false){
MessageBox(NULL, TEXT("unlock"),
NULL, NULL);
calljvm();
}
sleep=false;
break;
case WTS_SESSION_LOGON:
MessageBox(NULL, TEXT("log in"),
NULL, NULL);
calljvm();
break;
case WTS_SESSION_LOGOFF :
MessageBox(NULL, TEXT("log in"),
NULL, NULL);
calljvm();
break;
}
default:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void registerWndProc() {
HINSTANCE h2 = GetModuleHandle(NULL);
static char szAppName[] = "winhello";
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = h2;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszClassName = szAppName;
wndclass.lpszMenuName = NULL;
if (!RegisterClassEx(&wndclass)) {
MessageBox(NULL, TEXT("Registering class failed!"), TEXT("Error!"), MB_ICONEXCLAMATION | MB_OK);
return;
}
HWND hwnd;
hwnd = CreateWindow(szAppName, "Hello, world!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, h2, NULL);
if( !hwnd ) {
MessageBox(NULL, TEXT("Window creation failed!"), TEXT("Error!"), MB_ICONEXCLAMATION | MB_OK);
return;
}
ShowWindow(hwnd, 1);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
finaly compile HelloWorld.c and make dll.
note:
Beware that the JDK has a 32-bits and 64-bits version. The dll file must be compiled for the right architecture.
You must add wtsapi32.dll to java library project.
USEFUL RESOURCE:
The Java™ Native Interface Programmer’s Guide and Specification
https://www.itxplain.nl/blog/java-jni-wm_endsession-messages
https://www.apriorit.com/dev-blog/413-win-api-shutdown-events