📄 windowstrayiconmemoryleak.cpp
字号:
// Release java string
env->ReleaseStringUTFChars(tip, tooltip);
updateIcon(id_num);
}
/*
* Class: WindowsTrayIcon
* Method: freeIcon
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_jeans_trayicon_WindowsTrayIcon_freeIcon(JNIEnv *env, jclass, jint id_num) {
freeIcon(env, id_num);
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: initTrayIcon
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_jeans_trayicon_WindowsTrayIcon_initTrayIcon(JNIEnv *env, jclass, jstring wndName) {
if (my_hDlg == NULL) {
// Copy window name
const char *cWndName = env->GetStringUTFChars(wndName, 0);
strncpy(szWndName ,cWndName, WNDNAME_MAX);
szWndName[WNDNAME_MAX] = 0;
env->ReleaseStringUTFChars(wndName, cWndName);
// Initialize icon data struct
for (int ctr = 0; ctr < MY_MAX_ICONS; ctr++) tray_icons[ctr].used = FALSE;
// Popup invisible dummy window
if (g_hinst != NULL) {
wait_event = CreateEvent(NULL,FALSE,FALSE,NULL);
_beginthread(DialogThread, 0, NULL );
}
#ifdef USE_JVM_HINSTANCE
//Try to load Native part
hInstNative = LoadLibrary("npjava32");
#endif
}
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: isRunning
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT jboolean JNICALL Java_jeans_trayicon_WindowsTrayIcon_isRunning(JNIEnv *env, jclass, jstring wndName) {
const char *cWndName = env->GetStringUTFChars(wndName, 0);
// Find out if there's a hidden window with the given title
HWND mHwnd = FindWindow(szAppName, cWndName);
env->ReleaseStringUTFChars(wndName, cWndName);
// If there is, another instance of our app is already running
return mHwnd != NULL;
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: sendWindowsMessage
* Signature: (Ljava/lang/String;J)J
*/
JNIEXPORT jint JNICALL Java_jeans_trayicon_WindowsTrayIcon_sendWindowsMessage
(JNIEnv *env, jclass, jstring wndName, jint lParam) {
const char *cWndName = env->GetStringUTFChars(wndName, 0);
// Find hidden window handle by name
HWND mHwnd = FindWindow(szAppName, cWndName);
env->ReleaseStringUTFChars(wndName, cWndName);
// If the window exists, send out our message and wait for return value
if (mHwnd == NULL) return -1;
else return SendMessage(mHwnd, MYWM_APPTALK, 0, lParam);
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: cleanUp
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jeans_trayicon_WindowsTrayIcon_cleanUp(JNIEnv *env, jclass) {
cleanUpExit(env);
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: initPopup
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_jeans_trayicon_WindowsTrayIcon_initPopup
(JNIEnv *env, jclass, jint id_num, jint nbLevels) {
// Icon id valid?
if (tray_icons[id_num].used == FALSE) {
last_error = TRAY_WRONGICONID;
return;
}
// Free previous allocated menu
freeMenu(id_num);
// Create new popup menu with given depth
tray_icons[id_num].popup = new PopupMenu(nbLevels);
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: checkPopup
* Signature: (IIZ)V
*/
JNIEXPORT void JNICALL Java_jeans_trayicon_WindowsTrayIcon_checkPopup
(JNIEnv *env, jclass, jint id_num, jint menuId, jboolean selected) {
// Get main popup menu handle of icon
PopupMenu *popup = tray_icons[id_num].popup;
if (popup != NULL) {
HMENU menu = popup->getMenu(0);
// Add check mark or remove check mark
UINT how = selected ?
MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
// Check the menu item by command id (menuId)
CheckMenuItem(menu, menuId, how);
}
}
/*
* Class: jeans_trayicon_WindowsTrayIcon
* Method: subPopup
* Signature: (IILjava/lang/String;I)I
*/
JNIEXPORT jint JNICALL Java_jeans_trayicon_WindowsTrayIcon_subPopup
(JNIEnv *env, jclass, jint id_num, jint level, jstring menuName, jint type) {
// Return a menu id for the new submenu item
jint id = -1;
// Icon id valid?
if (tray_icons[id_num].used == FALSE) {
last_error = TRAY_WRONGICONID;
return -1;
}
// Popup valid? (use initPopup())
if (tray_icons[id_num].popup != NULL) {
const char *cMenuName;
PopupMenu *popup = tray_icons[id_num].popup;
if (type == POPUP_TYPE_INIT_LEVEL || type == POPUP_TYPE_DONE_LEVEL) {
// Add new level to the popup menu (= new submenu)
switch (type) {
case POPUP_TYPE_INIT_LEVEL:
// Marks the first item of the new level
popup->initMenu(level);
break;
case POPUP_TYPE_DONE_LEVEL:
// Marks the last item of the current level
if (level > 0) {
PopupSubMenu *menu = popup->getSubMenu(level);
HMENU hMenu = popup->getMenu(level-1);
HMENU sMenu = menu->getMenu();
// Get the name for the submenu
cMenuName = env->GetStringUTFChars(menuName, 0);
// Append the submenu to the menu one level back (thus the parent menu)
AppendMenu(hMenu,MF_POPUP | MF_ENABLED | MF_UNCHECKED, (UINT)sMenu, cMenuName);
env->ReleaseStringUTFChars(menuName, cMenuName);
// Sub menus must not be destroyed, only parents must: mark as sub
menu->makeSub();
}
break;
}
} else {
// Add a regular item to a (sub)menu
HMENU hMenu = popup->getMenu(level);
if (hMenu == NULL) return -1;
switch (type) {
case POPUP_TYPE_ITEM:
case POPUP_TYPE_CHECKBOX:
// Get menu item name (checkbox or simple menu item)
cMenuName = env->GetStringUTFChars(menuName, 0);
// Get free id for the new menu item
id = getFreeMenuId(id_num);
// Append the new item to the existing menu
AppendMenu(hMenu, MF_STRING | MF_ENABLED | MF_UNCHECKED, id, cMenuName);
env->ReleaseStringUTFChars(menuName, cMenuName);
break;
case POPUP_TYPE_SEPARATOR:
// Append a separator to the menu
AppendMenu(hMenu,MF_SEPARATOR, 0, NULL);
break;
}
}
}
// Return the id of the new menu item (used for callback messages)
return id;
}
// Main proc of DLL, called on initialisation, termination
BOOL WINAPI DllMain(HANDLE hInst, ULONG fdwReason, LPVOID lpReserved) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
// Store the instance handle
g_hinst = hInst;
// Make new map for menu ids
if (arrUsedMenuIds == NULL) arrUsedMenuIds = new QSIntArray();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
// Delete the map for menu ids
if (arrUsedMenuIds != NULL) {
delete arrUsedMenuIds;
arrUsedMenuIds = NULL;
}
break;
}
// Initialisation OK
return TRUE;
}
// This proc is called on exit (before Java's System.exit())
// Free all icon resources and remove the hidden window
void cleanUpExit(JNIEnv *env) {
for (int id_num = 0; id_num < MY_MAX_ICONS; id_num++) freeIcon(env, id_num);
RemoveHWND();
}
// Free menu resources for icon with given id
void freeMenu(int id_num) {
// Free allocated menu id's
setFreeMenuId(id_num);
// Free popup class
if (tray_icons[id_num].popup != NULL) {
delete tray_icons[id_num].popup;
tray_icons[id_num].popup = NULL;
}
}
// Free all icon resources for given id
// Make invisible, destroy icon, destroy tooltip, destroy global reference, free menu,..
void freeIcon(JNIEnv *env, int id_num) {
// Icon handle valid?
if (tray_icons[id_num].used == TRUE) {
// Make invisible
if (tray_icons[id_num].visible == TRUE) {
makeInvisible(id_num);
}
// Make invalid
tray_icons[id_num].used == FALSE;
// Free icon
if (tray_icons[id_num].icon != NULL) {
delete tray_icons[id_num].icon;
tray_icons[id_num].icon = NULL;
}
// Free tooltip
if (tray_icons[id_num].tooltip != NULL) {
delete tray_icons[id_num].tooltip;
tray_icons[id_num].tooltip = NULL;
}
// Free global ref to callback class for mouse/menu events
if (tray_icons[id_num].globalClass != 0) {
if (env != NULL) env->DeleteGlobalRef(tray_icons[id_num].globalClass);
else CallJavaVMS(DeleteGlobalCallback, id_num, 0);
tray_icons[id_num].globalClass = 0;
}
// Free menu ids and resources
freeMenu(id_num);
}
}
// Update icon with given id in system tray
// Show or Hide and add tooltip,..
void updateIcon(jint id_num) {
// Valid hidden window handle, icon id an visible?
if (my_hDlg != NULL && tray_icons[id_num].used == TRUE && tray_icons[id_num].visible == TRUE) {
// Valid instance handle and icon resources?
if (g_hinst != NULL && tray_icons[id_num].icon != NULL) {
// Get icon handle
HICON icon = tray_icons[id_num].icon->makeIcon(g_hinst);
if (icon != NULL) {
// Modify icon status
TrayMessage(my_hDlg, NIM_MODIFY, id_num, icon, tray_icons[id_num].tooltip);
} else {
last_error = TRAY_NOTENOUGHMEM;
}
} else {
// Make icon invisible if no valid resources
makeInvisible(id_num);
}
}
}
// Hide icon
void makeInvisible(jint id_num) {
// Valid icon id and currently visible?
if (tray_icons[id_num].used == TRUE && tray_icons[id_num].visible == TRUE) {
// Make invisible
if (my_hDlg != NULL) TrayMessage(my_hDlg, NIM_DELETE, id_num, NULL, NULL);
tray_icons[id_num].visible = FALSE;
}
}
// Add/Remove/Modify tray icon to system tray
BOOL TrayMessage(HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, PSTR pszTip) {
BOOL res;
// Fill data struct for tray icon
NOTIFYICONDATA tnd;
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = hDlg;
tnd.uID = uID;
tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnd.uCallbackMessage = MYWM_NOTIFYICON;
tnd.hIcon = hIcon;
// Include tooltip?
if (pszTip) {
lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip));
} else {
tnd.szTip[0] = '\0';
}
// Call tray icon windows API function
res = Shell_NotifyIcon(dwMessage, &tnd);
// Destroy the icon's handle (icon data is copied by Windows function)
if (hIcon) DestroyIcon(hIcon);
return res;
}
// Java VM callback function to delete a global reference to a given Java class
// Used to delete the global reference to the icon's class to receive mouse/menu events
int DeleteGlobalCallback(JNIEnv *env, int id_num, int dummy) {
env->DeleteGlobalRef(tray_icons[id_num].globalClass);
tray_icons[id_num].globalClass = 0;
return 0;
}
// Java VM callback function used to notify icon class after incomming sendWindowsMessage()
int WindowsMessageCallback(JNIEnv *env, int dummy, int wParam) {
// Get reference to WindowsTrayIcon Java class
jclass cls = env->FindClass("jeans/trayicon/WindowsTrayIcon");
if (cls == 0) return -1;
// Get static callback method id
jmethodID mid = env->GetStaticMethodID(cls, "callWindowsMessage", "(I)I");
if (mid == 0) return -1;
// Make call to "callWindowsMessage" with parameter wParam
return env->CallStaticIntMethod(cls, mid, (jint)wParam);
}
// Java VM callback function used for menu item callbacks
int MenuItemCallback(JNIEnv *env, int id_num, int menu_id) {
// Valid icon id and valid global reference to icon's Java class?
if (tray_icons[id_num].used == FALSE) return TRAY_WRONGICONID;
jobject obj = tray_icons[id_num].globalClass;
if (obj == 0) return TRAY_NOLISTENER;
jclass winTrayClass = env->GetObjectClass(obj);
if (winTrayClass == 0) return TRAY_NOTENOUGHMEM;
// Get callback method id
jmethodID mid = env->GetMethodID(winTrayClass, "notifyMenuListeners", "(I)V");
if (mid == 0) return TRAY_METHODID;
// Call method "notifyMenuListeners"
env->CallVoidMethod(obj, mid, menu_id);
return TRAY_NOERR;
}
// Java VM callback used for mouse pressed callbacks
int MousePressedCallback(JNIEnv *env, int id_num, int button) {
// Valid icon id and valid global reference to icon's Java class?
if (tray_icons[id_num].used == FALSE) return TRAY_WRONGICONID;
jobject obj = tray_icons[id_num].globalClass;
if (obj == 0) return TRAY_NOLISTENER;
jclass winTrayClass = env->GetObjectClass(obj);
if (winTrayClass == 0) return TRAY_NOTENOUGHMEM;
// Get callback method id
jmethodID mid = env->GetMethodID(winTrayClass, "notifyMouseListeners", "(I)V");
if (mid == 0) return TRAY_METHODID;
// Call method "notifyMouseListeners"
env->CallVoidMethod(obj, mid, button);
return TRAY_NOERR;
}
// Call a Java method in a given virtual machine
int CallJavaVM(JavaVM* vm, JNIProcPtr(JNIProc), int arg1, int arg2) {
int result = TRAY_NOERR;
#ifdef HAS_JVM_LIB
JNIEnv* env;
// Attach current thread to given Java VM
if (vm->AttachCurrentThread(&env, NULL) < 0) return TRAY_ERRTHREAD;
// Call method (MousePressedCallback/MenuItemCallback/WindowsMessageCallback/..)
result = (*JNIProc)(env, arg1, arg2);
// Check for exception detach thread and exit
if (env->ExceptionOccurred()) env->ExceptionDescribe();
vm->DetachCurrentThread();
#endif
return result;
}
// Call a Java method in all available VM's (used for mouse/menu callbacks)
int CallJavaVMS(JNIProcPtr(JNIProc), int arg1, int arg2) {
int value = TRAY_NOERR;
JavaVM *vm_ptr;
jsize number;
#ifdef HAS_JVM_LIB
// Check for existing Java virtual machines
if (JNI_GetCreatedJavaVMs(NULL, 0, &number) != 0) return TRAY_JNIERR;
if (number == 0) return TRAY_NOVMS;
// Call method in each available VM
for (int ctr = 0; ctr < number; ctr++) {
if (JNI_GetCreatedJavaVMs(&vm_ptr, ctr+1, NULL) != 0) return TRAY_JNIERR;
int res = CallJavaVM(vm_ptr, JNIProc, arg1, arg2);
if (res != TRAY_NOERR) value = res;
}
#endif
#ifdef USE_JVM_HINSTANCE
// Get addr of "JNI_GetCreatedJavaVMs"
jint (FAR* lpfnJNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *);
lpfnJNI_GetCreatedJavaVMs =
(jint (FAR*) (JavaVM **, jsize, jsize *))GetProcAddress(hInstNative, "JNI_GetCreatedJavaVMs");
if (lpfnJNI_GetCreatedJavaVMs == NULL) return TRAY_CALLBACKDLLERR;
// Check for existing Java virtual machines
if ((*lpfnJNI_GetCreatedJavaVMs)(NULL, 0, &number) != 0) return TRAY_JNIERR;
if (number == 0) return TRAY_NOVMS;
// Call method in each available VM
for (int ctr = 0; ctr < number; ctr++) {
if ((*lpfnJNI_GetCreatedJavaVMs)(&vm_ptr, ctr+1, NULL) != 0) return TRAY_JNIERR;
int res = CallJavaVM(vm_ptr, JNIProc, arg1, arg2);
if (res != TRAY_NOERR) value = res;
}
#endif
return value;
}
// Thread proc to call Java method (calls CallJavaVMS but with wrapped params)
void CallJavaThread(void *arg) {
ThreadJavaCallback *tjc = (ThreadJavaCallback*)arg;
int result = CallJavaVMS(tjc->jni_proc, tjc->arg1, tjc->arg2);
if (result != TRAY_NOERR) last_error = result;
delete tjc;
}
// Call a Java method in a new thread
void CallJavaVMSThread(JNIProcPtr(JNIProc), int arg1, int arg2) {
// Wrap parameters in struct
ThreadJavaCallback *tjc = new ThreadJavaCallback;
tjc->jni_proc = JNIProc;
tjc->arg1 = arg1;
tjc->arg2 = arg2;
// Create new thread and call "CallJavaThread()"
if (_beginthread(CallJavaThread, 0, tjc) == -1) delete tjc;
}
// Handle popup menu command
void HandleMenuCommand(WPARAM menuId) {
// Get icon id given menu id
int id_num = getMenuItemIdNum(menuId);
// Callback to Java class in new thread, using method "MenuItemCallback()"
CallJavaVMSThread(MenuItemCallback, id_num, menuId);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -