使用Java实现Workstation锁定侦听器

时间:2014-09-08 15:34:28

标签: java swing jna

当我们同时按下WIN_HOME + L键时窗口被锁定。我们可以在VB中找到很好的例子来监听窗口锁定事件。但我正在为Java做窗口锁定监听器。我做了一些研究并制作了一个监听窗口锁定事件的监听程序。它有两个接口和一个带JFrame的主程序,并使用JNA库。

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);
}

主要类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);
}
}

如果我们的框架处于可见状态,则上面的代码效果很好,但当框架可见性设置为false或框架被图标化时,上述代码不起作用。

如果框架被图标化,我们如何实现上面的监听器?

2 个答案:

答案 0 :(得分:3)

它似乎适用于隐藏窗口"如JNA' Win32WindowDemo所示。因此,而不是附加一个"本机Windows事件监听器"到JFrame,使用"隐藏窗口"作为后台服务。

我将Win32WindowDemo剥离到与问题相关的部分,并添加了一些部分来启动和停止它作为后台服务。请注意,创建HWND的线程也必须用于读取HWND的消息(如果另一个线程尝试读取消息,则没有任何反应)。

首先显示带有来自Win32WindowDemo的消息的JFrame的主类:

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);
}

}

接下来将Win32WindowDemo的精简版本作为后台服务:

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;
    }
}

}

我使用WINHOME_KEY + L在Windows Vista 64bit上使用JNA 4.1.0(从here下载)进行了测试。 "隐藏窗口的背景线程"使用shutdown-trigger停止,当主类的JFrame关闭时,该触发器被激活。

答案 1 :(得分:-1)

我认为你想在后台运行任务,所以我强烈建议使用swingworker,因此在这种情况下框架是否设置可见是否真实并不重要。此外,您还应该为窗口活动或状态选择特定事件。如果您对窗口进行图标化,并且仍想运行任务,则在该事件下复制代码。

Window activities or states can precede a window event:

请参阅一些示例如何实施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