您的位置:首页 > 编程语言 > Java开发

解决Java程序的托盘图标在Windows任务栏重建后丢失的问题(使用SystemTray类创建托盘图标)

2012-04-26 18:34 801 查看
JavaSE 1.6提供了java.awt.SystemTray类用于方便地创建托盘图标.但在Windows平台下当explorer崩溃时托盘图标会丢失.

如果是本地代码或.Net平台的程序则可以很简单地获取TaskbarCreated消息并重新添加托盘图标.

但在Java程序中无法直接访问Windows消息.

解决方法是通过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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐