JNA console close listener

0

In the last example we saw a simple example of JNA.

In this example we will see how to add a console control handler. Imagine an application running in a console window. We want to gracefully exit when the console window is closed.

The native library to load is windows Kernel32. In order to add console handler, we need to call native function SetConsoleCtrlHandler().

We first need to add the native method SetConsoleCtrlHandler() to the class trying to register the native library.

This will in turn map the native methods found within that class to native libraries via the JNA raw calling interface.

public class ThreadAliveExample {
...
    native boolean SetConsoleCtrlHandler(StdCallLibrary.StdCallCallback handler, boolean add);

    public ThreadAliveExample() {
...
        try {
            Native.register("kernel32");
...
            ConsoleHandlerCallback callback = new ConsoleHandlerCallback(new ConsolelHandler() {
                @Override
                public boolean handle(int code) {
                   ...
                }
            });
            SetConsoleCtrlHandler(callback, true);
        } catch (NoClassDefFoundError e) {
            System.out.println("JNA not found. native methods and handlers will be disabled.");
        } catch (UnsatisfiedLinkError e) {
            System.out.println("unable to link Windows/Kernel32 library. native methods and handlers will be disabled.");
        }
    }
...
}

Below class is a console callback class to handle control event. It implements StdCallLibrary.StdCallCallback which in turn extends Library and Callback.
We need to create an instance of the console handler and call the native method declared. This in turn results in native registering the callback, mapping to a pointer within the target native library.

SetConsoleCtrlHandler(callback, true);

ConsoleHandlerCallback:

public class ConsoleHandlerCallback implements StdCallLibrary.StdCallCallback {

    private final ConsolelHandler handler;

    ConsoleHandlerCallback(ConsolelHandler handler) {
        this.handler = handler;
    }

    public boolean callback(long dwCtrlType) {
        int event = (int) dwCtrlType;
        System.out.println("Console handler receives event " + event);
        return handler.handle(event);
    }
}

ConsolelHandler:

package com.javarticles.threads;

public interface ConsolelHandler {

    int CTRL_CLOSE_EVENT = 2;

    boolean handle(int code);
}

Complete example is here.

ThreadAliveExample:

package com.javarticles.threads;

import java.util.concurrent.CountDownLatch;

import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;

public class ThreadAliveExample {
    private final Thread aliveThread;
    private final CountDownLatch aliveLatch = new CountDownLatch(1);
    native boolean SetConsoleCtrlHandler(StdCallLibrary.StdCallCallback handler, boolean add);

    public ThreadAliveExample() {
        aliveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    aliveLatch.await();
                } catch (InterruptedException e) {
                }
            }
        }, "AliveThread");
        aliveThread.setDaemon(false);
 
        try {
            Native.register("kernel32");
            System.out.println("windows/Kernel32 library loaded");
            ConsoleHandlerCallback callback = new ConsoleHandlerCallback(new ConsolelHandler() {
                @Override
                public boolean handle(int code) {
                    System.out.println("Code: " + code);
                    if (CTRL_CLOSE_EVENT == code) {
                        System.out.println("running graceful exit on windows");
                        aliveLatch.countDown();
                        return true;
                    }
                    return false;
                }
            });
            SetConsoleCtrlHandler(callback, true);
        } catch (NoClassDefFoundError e) {
            System.out.println("JNA not found. native methods and handlers will be disabled.");
        } catch (UnsatisfiedLinkError e) {
            System.out.println("unable to link Windows/Kernel32 library. native methods and handlers will be disabled.");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ThreadAliveExample threadAlive = new ThreadAliveExample();
        threadAlive.aliveThread.start();
    }
}

Even if we close the console window, the application takes care of a graceful exit.
java -cp javaThreadGroupExample-4.0.0.jar com.javarticles.threads.ThreadAliveExample>output.txt
Output:

windows/Kernel32 library loaded
Console handler receives event 2
Code: 2
running graceful exit on windows

Download the source code

This was an example about using JNA to handle console window close event.

You can download the source code here: jnaConsoleHandler.zip

About Author

Ram's expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring. You can reach him at [email protected]

Comments are closed.