解决方法是通过JNI调用本地代码安装消息钩子捕获TaskbarCreated消息并通知主程序重建图标.
Java部分
package idv.takamachi660.platform.win; import java.util.ArrayList; import java.util.List; /** * 任务栏重建监视器 * * @author Takamachi660 * */ public class TaskBarMonitor { private static TaskBarMonitor instance; private boolean enabled; private long lastMsg; private int minimumInterval = 1500; private final List<TaskBarListener> listeners = new ArrayList<TaskBarListener>(); static { try { System.loadLibrary("TaskBarMonitor"); // 如果本地库成功加载则创建实例 instance = new TaskBarMonitor(); } catch (Throwable ex) { ex.printStackTrace(); } } /** * 查询监视器是否可用 */ public static boolean isSupported() { return instance != null; } /** * 返回全局唯一实例,若不可用则返回null */ public static TaskBarMonitor getInstance() { return instance; } private TaskBarMonitor() { } /** * 本地方法,安装钩子,保存必要的全局引用 */ private native boolean installHook(); /** * 本地方法,撤销钩子,释放全局引用 */ private native boolean unInstallHook(); /** * 从钩子处理函数调用 */ @SuppressWarnings("unused") private void hookCallback() { long current = System.currentTimeMillis(); if (current - this.lastMsg >= this.getMinimumInterval()) for (TaskBarListener l : listeners) l.taskBarCreated(); this.lastMsg = current; } /** * 查询监视器状态(钩子是否已经安装) */ public boolean isEnabled() { return enabled; } /** * 设置监视器状态(安装或撤销钩子),若与当前状态相同则不执行任何操作 */ public void setEnable(boolean enable) { if (this.isEnabled() != enable) { if (enable) this.enabled = this.installHook(); else this.enabled = !this.unInstallHook(); } } /** * 设置最小消息触发间隔(防止重复) */ public void setMinimumInterval(int minimumInterval) { this.minimumInterval = minimumInterval; } /** * 获取最小消息触发间隔 */ public int getMinimumInterval() { return minimumInterval; } public void addTaskBarListener(TaskBarListener listener) { listeners.add(listener); } public void removeTaskBarListener(TaskBarListener listener) { listeners.remove(listener); } @Override protected void finalize() throws Throwable { this.setEnable(false); // 防止忘记调用setEnable(false)造成内存泄漏 // 但不可靠 }
本地部分
#include "idv_takamachi660_platform_win_TaskBarMonitor.h" #include <windows.h> #include <tchar.h> //全局变量 HINSTANCE hInstance;//保存DLL实例句柄 HHOOK hHook;//钩子句柄 int WM_TASKBARCREATED;//TaskbarCreated消息代码 jobject jobj;//回调目标的Java对象 JavaVM *jvm;//Java虚拟机指针 jint jniVersion;//JNI版本 jmethodID jCallbackMid;//回调方法指针 LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) {//钩子处理函数,判断消息是否为TaskbarCreated,若是则回调Java方法 if(jvm) { CWPSTRUCT* pMsg=(CWPSTRUCT*)lParam; if(pMsg->message==WM_TASKBARCREATED) { JNIEnv *env;//获取当前线程(应该会是AWT消息循环线程)的JNIEnv if(JNI_OK==jvm->GetEnv((void **)&env,jniVersion)) env->CallVoidMethod(jobj,jCallbackMid);//回调Java方法 } } return CallNextHookEx(hHook,nCode,wParam,lParam); } JNIEXPORT jboolean JNICALL Java_idv_takamachi660_platform_win_TaskBarMonitor_installHook (JNIEnv *env, jobject obj) {//安装钩子,保存必要的全局引用 WM_TASKBARCREATED=RegisterWindowMessage(_T("TaskbarCreated")); hHook=SetWindowsHookEx(WH_CALLWNDPROC,HookProc,hInstance,0); if(hHook && WM_TASKBARCREATED) { jclass cls = env->GetObjectClass(obj);//获取监视器的Java类 if(cls) { jmethodID tempMid=env->GetMethodID(cls,"hookCallback","()V");//获取回调方法的局部引用 if(tempMid) { jniVersion=env->GetVersion();//获取JNI版本号 env->GetJavaVM(&jvm);//保存JVM指针 jobj=env->NewGlobalRef(obj);//创建监视器对象的全局引用 jCallbackMid=(jmethodID)env->NewGlobalRef((jobject)tempMid); //创建回调方法的全局引用 return (jboolean)1; } } } return (jboolean)0; } JNIEXPORT jboolean JNICALL Java_idv_takamachi660_platform_win_TaskBarMonitor_unInstallHook (JNIEnv *env, jobject obj) {//撤销钩子,释放全局引用 if(hHook) { if(jobj) env->DeleteGlobalRef(jobj); if(jCallbackMid) env->DeleteGlobalRef((jobject)jCallbackMid); return (jboolean)UnhookWindowsHookEx(hHook); } else return (jboolean)0; } BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) {//DLL入口函数,保存DLL实例句柄 if(fdwReason==DLL_PROCESS_ATTACH) { hInstance=hinstDLL; } return true;
测试 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->import idv.takamachi660.platform.win.TaskBarListener; import idv.takamachi660.platform.win.TaskBarMonitor; import java.awt.AWTException; import java.awt.Image; import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.URL; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class TestTaskBarMonitor extends JFrame { private static final long serialVersionUID = 6600311070370645769L; private Image iconImg; private TrayIcon trayIcon; public TestTaskBarMonitor() { super("TestTaskBarMonitor"); this.setBounds(0, 0, 160, 90); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); if (SystemTray.isSupported()) { URL iconUrl = ClassLoader.getSystemResource("icon.png"); iconImg = Toolkit.getDefaultToolkit().createImage(iconUrl); trayIcon = new TrayIcon(iconImg, "TestTaskBarMonitor"); trayIcon.setImageAutoSize(true); this.addTrayIcon(); if (TaskBarMonitor.isSupported()) { TaskBarMonitor.getInstance().addTaskBarListener( new TaskBarListener() { @Override public void taskBarCreated() { SwingUtilities.invokeLater(new Runnable() { // 一定要在Swing事件处理线程中调用 // 直接在AWT事件循环线程中调用没有效果 @Override public void run() { addTrayIcon(); } }); } }); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { // 记得在程序退出时手动关闭,否则会造成内存泄漏 TaskBarMonitor.getInstance().setEnable(false); } }); // 启动监视器 TaskBarMonitor.getInstance().setEnable(true); } } } private void addTrayIcon() { System.out.println("addTrayIcon called"); try { SystemTray tray = SystemTray.getSystemTray(); // 总会得到同一个SystemTray对象 tray.remove(trayIcon);// 一定要先remove tray.add(trayIcon); } catch (AWTException e) { e.printStackTrace(); } } public static void main(String[] args) { new TestTaskBarMonitor().setVisible(true); } }