3

Window get locked when we press WIN_HOME+L key together. We can find lot good example to listen to window lock event in VB. But I am doing window lock listener for Java. I've done some research and made a listener program that listen to window lock event. It has two interface and one main program with JFrame and uses JNA library.

WindowUser32.java

public interface WindowUser32 extends User32
{   public static final WindowUser32 MYINSTANCE = (WindowUser32) Native.loadLibrary("user32",  WindowUser32.class, W32APIOptions.UNICODE_OPTIONS);
public int SetWindowLong(WinDef.HWND hWnd, int nIndex, Callback callback);
}

WindowListener.java

public interface WindowListener extends StdCallCallback
{
public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);
}

And main class LockListener.java

public class LockListener
{

public static void main(String[] args)
    {   
        JFrame frame = new JFrame();
    frame.setVisible(true);

    HWND hwnd = new HWND();
    hwnd.setPointer(Native.getWindowPointer(frame));

    Wtsapi32.INSTANCE.WTSRegisterSessionNotification(hwnd, Wtsapi32.NOTIFY_FOR_ALL_SESSIONS);

    WindowListener listener = new WindowListener()
    {
        @Override
        public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
        {
            if (uMsg == WinUser.WM_SESSION_CHANGE)
            {
                switch (wParam.intValue())
                {
                    case Wtsapi32.WTS_SESSION_LOCK:
                        System.out.println("Locked " + new Date());
                    break;

                    case Wtsapi32.WTS_SESSION_UNLOCK:
                        System.out.println("Unlocked "  + new Date());
                    break;
                }
            }
            return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
    };

    WindowUser32.MYINSTANCE.SetWindowLong(hwnd, WindowUser32.GWL_WNDPROC, listener);

    // Wtsapi32.INSTANCE.WTSUnRegisterSessionNotification(hwnd);
}
}

Above code works great if our frame in visible, but doesnt work when frame visibility is set to false or frame is iconified.

How can we implement above listener if frame is iconified?

Crawler
  • 1,988
  • 3
  • 21
  • 42
  • 1
    To the best of my knowledge, by not using Java. – Elliott Frisch Sep 08 '14 at 15:39
  • I've you seen [this answer](http://stackoverflow.com/a/23853461/773623)? – Jonathan Drapeau Sep 08 '14 at 15:43
  • I've tried all those but it didn't solve my problem. And more they are suggesting not to use Java but I need to. – Crawler Sep 08 '14 at 15:52
  • @ElliottFrisch do you have any idea how to access windows dll using jna? Any little tricks or knowledge that I've not tried yet. – Crawler Sep 08 '14 at 15:58
  • @ElliottFrisch, I knw that I've already accessed windows dll, but Im new to JNA, so can you explain why we can capture window lock event if our JFrame isn't visible? – Crawler Sep 08 '14 at 16:44
  • @ElliottFrisch, using JNA I've already captured keystrokes, mouse clicks, processes list and active window name and it works even if frame is iconified. Only problem I'm having is with Window Lock that it doesn't work when iconified. So I think I'm missing something. – Crawler Sep 08 '14 at 17:12
  • I think I found relevant [demo source code](https://github.com/twall/jna/blob/master/contrib/native_window_msg/src/com/sun/jna/platform/win32/Win32WindowDemo.java). It could help you find what does and does not work (e.g. `NOTIFY_FOR_THIS_SESSION` used in demo source code versus `NOTIFY_FOR_ALL_SESSIONS` in your source code). – vanOekel Sep 12 '14 at 10:38

2 Answers2

4

It appears to work with the "hidden window" as shown in the JNA's Win32WindowDemo. So instead of attaching a "native windows event listener" to a JFrame, use the "hidden window" as a background service.

I stripped down the Win32WindowDemo to the parts relevant for the question and added some parts to start and stop it as a background service. Note that the thread that creates the HWND must also be used to read messages for the HWND (if another thread tries to read the messages, nothing happens).

First the main class showing a JFrame with the messages from the Win32WindowDemo:

import java.awt.*;
import javax.swing.*;

public class WinLockDetect {

public static void main(String[] args) {

    try {
        new WinLockDetect().demo();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@SuppressWarnings("serial")
void demo() throws Exception {

    final JTextArea detectMsgs =new JTextArea();
    final JFrame window = new JFrame() {{
        getContentPane().add(detectMsgs);
        setSize(400, 200);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }};
    EventQueue.invokeLater(new Runnable() {
        public void run() { 
            window.setVisible(true); 
        };
    });
    Win32EventDetector winSessionDetect = new Win32EventDetector();
    winSessionDetect.setMsgLogger(detectMsgs);
    new Thread(winSessionDetect).start();
    Thread.sleep(500L);
    window.setState(java.awt.Frame.ICONIFIED);
}

}

Next the stripped down version of Win32WindowDemo as a background service:

import java.util.concurrent.*;
import javax.swing.JTextArea;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinUser.*;

public class Win32EventDetector implements WindowProc, Runnable {


/** FIXME: don't know if this number is a real message ID. */
private static final int DESTROY_LISTENER = 4242; 

volatile boolean closed = true;
Semaphore closing = new Semaphore(0);
WString windowClass;
HMODULE hInst;
HWND hWnd;
JTextArea detectMsgs;

public void setMsgLogger(JTextArea detectMsgs) {
    this.detectMsgs = detectMsgs;
}

void println(String msg) {

    if (detectMsgs == null) {
        System.out.println(msg);
    } else {
        detectMsgs.append('\n' + msg);
        detectMsgs.setCaretPosition(detectMsgs.getDocument().getLength());
    }
}

@Override
public void run() {

    // define new window class
    windowClass = new WString("Win32EventDetectorClass");
    hInst = Kernel32.INSTANCE.GetModuleHandle("");
    WNDCLASSEX wClass = new WNDCLASSEX();
    wClass.hInstance = hInst;
    wClass.lpfnWndProc = Win32EventDetector.this;
    wClass.lpszClassName = windowClass;
    // register window class
    User32.INSTANCE.RegisterClassEx(wClass);
    getLastError();
    // create new window
    hWnd = User32.INSTANCE
            .CreateWindowEx(
                    User32.WS_EX_TOPMOST,
                    windowClass,
                    "My hidden helper window, used only to catch the windows events",
                    0, 0, 0, 0, 0,
                    null, // WM_DEVICECHANGE contradicts parent=WinUser.HWND_MESSAGE
                    null, hInst, null);
    getLastError();
    println("window sucessfully created! window hwnd: "
            + hWnd.getPointer().toString());

    closed = false;
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() { close(); }
    });

    Wtsapi32.INSTANCE.WTSRegisterSessionNotification(hWnd,
            Wtsapi32.NOTIFY_FOR_THIS_SESSION);
    println("Listening for window messages.");
    try {
        MSG msg = new MSG();
        while (User32.INSTANCE.GetMessage(msg, hWnd, 0, 0) != 0) {
            if (msg.message == DESTROY_LISTENER) {
                System.out.println("Got destroy message.");
                break;
            }
            println("Got a new message: " + msg.message);
            User32.INSTANCE.TranslateMessage(msg);
            User32.INSTANCE.DispatchMessage(msg);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("Stopped listening for window messages.");
    destroy();
}

private void destroy() {

    try {
        Wtsapi32.INSTANCE.WTSUnRegisterSessionNotification(hWnd);
        User32.INSTANCE.UnregisterClass(windowClass, hInst);
        User32.INSTANCE.DestroyWindow(hWnd);
        System.out.println("Hidden native window destroyed.");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        closed = true;
        closing.release();
    }
}

public void close() {

    if (closed) { return; }
    User32.INSTANCE.PostMessage(hWnd, DESTROY_LISTENER, null, null);
    try {
        if (closing.tryAcquire(1000L, TimeUnit.MILLISECONDS)) {
            System.out.println("Hidden native window closed.");
        } else {
            System.out.println("Hidden native window could not be closed within time-out.");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WinUser.WM_CREATE: {
        println("onCreate: WM_CREATE");
        return new LRESULT(0);
    }
    case WinUser.WM_DESTROY: {
        User32.INSTANCE.PostQuitMessage(0);
        return new LRESULT(0);
    }
    case WinUser.WM_SESSION_CHANGE: {
        this.onSessionChange(wParam, lParam);
        return new LRESULT(0);
    }
    default:
        return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

public int getLastError() {
    int rc = Kernel32.INSTANCE.GetLastError();
    if (rc != 0)
        println("error: " + rc);
    return rc;
}

protected void onSessionChange(WPARAM wParam, LPARAM lParam) {
    switch (wParam.intValue()) {
    case Wtsapi32.WTS_CONSOLE_CONNECT: {
        println("WTS_CONSOLE_CONNECT");
        break;
    }
    case Wtsapi32.WTS_CONSOLE_DISCONNECT: {
        println("WTS_CONSOLE_DISCONNECT");
        break;
    }
    case Wtsapi32.WTS_SESSION_LOGON: {
        println("WTS_SESSION_LOGON");
        break;
    }
    case Wtsapi32.WTS_SESSION_LOGOFF: {
        println("WTS_SESSION_LOGOFF");
        break;
    }
    case Wtsapi32.WTS_SESSION_LOCK: {
        println("WTS_SESSION_LOCK");
        break;
    }
    case Wtsapi32.WTS_SESSION_UNLOCK: {
        println("WTS_SESSION_UNLOCK");
        break;
    }
    default: 
        println("Session change " + wParam.intValue());
        break;
    }
}

}

I tested this with JNA 4.1.0 (downloaded from here) on Windows Vista 64bit using the WINHOME_KEY+L. The background thread for the "hidden window" is stopped using the shutdown-trigger which is activated when the JFrame from the main class is closed.

vanOekel
  • 6,358
  • 1
  • 21
  • 56
  • Working with the hidden window... that's what I was missing... I got to make some changes for integration but hidden window concept was all I need. Thanks for your answer. – Crawler Sep 16 '14 at 11:37
-1

I think you want to run the task at background , So I strongly advice to use swingworker for this so it doesn't matter frame is to set visible true or not in this case. In addition You should also choose specific event for window activities or states. If you iconify the window and you want to still run the tasks then copy your code under that event.

Window activities or states can precede a window event:

Please see some samples how to implement swingworker

How do I make my SwingWorker example work properly?

Advanced Java: Multi-threading Part 15 -- Swing and the SwingWorker Class

How do I use SwingWorker in Java?

Java GUI threads - SwingWorker

Community
  • 1
  • 1
mussdroid
  • 732
  • 1
  • 9
  • 22
  • SwingWorker sounds interesting, but it require some research and then implementation. I will try it and reply if it solve my issue. – Crawler Sep 12 '14 at 10:50
  • I also tried create sample application which creates random numbers i set button starts the random number generator which generates number using system.out and my gui setvisible the false i see that the program is still working , i think you are exiting from the program or from the event so after that you program stops working since you didnt provide the Jframe class it is a little bit difficult to understand what is going on with it. – mussdroid Sep 12 '14 at 11:01
  • when window is setvisible(false) , the status of window changes so the event is changing actually since event is changed your current events stops functioning. events are changing since your code is not exits under windowclosed event maybe it stops functioning , i think you should carry your task under a method which can be called after frame is setvisible(false) – mussdroid Sep 12 '14 at 11:02