#include "stdafx.h" #include "trayicon.h" #include #include // for AfxLoadString IMPLEMENT_DYNAMIC(ITrayIcon, CCmdTarget) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); ITrayIcon::ITrayIcon(UINT uID) { // Initialize NOTIFYICONDATA memset(&m_nid, 0 , sizeof(m_nid)); m_nid.cbSize = sizeof(m_nid); m_nid.uID = uID; // never changes after construction OpendThread(); bShowMinibox = FALSE; //m_pWnd = AfxGetMainWnd(); // Use resource string as tip if there is one AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip)); } ITrayIcon::~ITrayIcon() { SetIcon(0); // remove icon from system tray TerminalThread(); } ////////////////// // Set notification window. It must created already. // void ITrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg) { // If the following assert fails, you're probably // calling me before you created your window. Oops. ASSERT(pNotifyWnd==NULL || ::IsWindow(pNotifyWnd->GetSafeHwnd())); m_nid.hWnd = pNotifyWnd->GetSafeHwnd(); ASSERT(uCbMsg==0 || uCbMsg>=WM_USER); m_nid.uCallbackMessage = uCbMsg; } ////////////////// // This is the main variant for setting the icon. // Sets both the icon and tooltip from resource ID // To remove the icon, call SetIcon(0) // BOOL ITrayIcon::SetIcon(UINT uID) { HICON hicon=NULL; if (uID) hicon = AfxGetApp()->LoadIcon(uID); UINT msg; m_nid.uFlags = 0; // Set the icon if (hicon) { // Add or replace icon in system tray msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD; m_nid.hIcon = hicon; m_nid.uFlags |= NIF_ICON; } else { // remove icon from tray if (m_nid.hIcon==NULL) return TRUE; // already deleted msg = NIM_DELETE; } // Use callback if any if (m_nid.uCallbackMessage && m_nid.hWnd) m_nid.uFlags |= NIF_MESSAGE; // Do it BOOL bRet = Shell_NotifyIcon(msg, &m_nid); if (msg==NIM_DELETE || !bRet) m_nid.hIcon = NULL; // failed return bRet; } BOOL ITrayIcon::SetIcon(UINT uID, LPCTSTR lpTip) { HICON hicon=NULL; if (uID) { AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip)); hicon = AfxGetApp()->LoadIcon(uID); } return SetIcon(hicon, lpTip ? lpTip:NULL); } ////////////////// // Common SetIcon for all overloads. // BOOL ITrayIcon::SetIcon(HICON hicon, LPCTSTR lpTip) { UINT msg; m_nid.uFlags = 0; // Set the icon if (hicon) { // Add or replace icon in system tray msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD; m_nid.hIcon = hicon; m_nid.uFlags |= NIF_ICON; } else { // remove icon from tray if (m_nid.hIcon==NULL) return TRUE; // already deleted msg = NIM_DELETE; } // Use the tip, if any if (lpTip) //strncpy(m_nid.szTip, lpTip, sizeof(m_nid.szTip)); StringCchCopy(m_nid.szTip, sizeof(m_nid.szTip),lpTip); if (m_nid.szTip[0]) m_nid.uFlags |= NIF_TIP; // Use callback if any if (m_nid.uCallbackMessage && m_nid.hWnd) m_nid.uFlags |= NIF_MESSAGE; // Do it BOOL bRet = Shell_NotifyIcon(msg, &m_nid); if (msg==NIM_DELETE || !bRet) m_nid.hIcon = NULL; // failed return bRet; } BOOL ITrayIcon::SetTip(LPCTSTR lpTip) { UINT msg; m_nid.uFlags = 0; if (m_nid.hIcon == NULL) return TRUE; // already deleted msg = NIM_MODIFY; // Use the tip, if any //if (lpTip) //strncpy(m_nid.szTip, lpTip, sizeof(m_nid.szTip)); StringCchCopy(m_nid.szTip, sizeof(m_nid.szTip), lpTip); if (m_nid.szTip[0]) m_nid.uFlags |= NIF_TIP; // Use callback if any if (m_nid.uCallbackMessage && m_nid.hWnd) m_nid.uFlags |= NIF_MESSAGE; return Shell_NotifyIcon(msg, &m_nid); } //BOOL ITrayIcon::SetInfo(LPCSTR lpInof, LPCSTR lpInfoTitle) BOOL ITrayIcon::SetInfo(LPCTSTR lpInof, LPCTSTR lpInfoTitle) { UINT msg; m_nid.uFlags = 0; if (m_nid.hIcon == NULL) return TRUE; // already deleted msg = NIM_MODIFY; //strncpy(m_nid.szInfo, lpInof, sizeof(m_nid.szInfo)); StringCchCopy(m_nid.szInfo, sizeof(m_nid.szInfo),lpInof); if ( lpInfoTitle != NULL) { StringCchCopy(m_nid.szInfoTitle, sizeof(m_nid.szInfoTitle), lpInfoTitle); m_nid.uFlags |= NIF_INFO|NIIF_INFO; m_nid.uTimeout = 1000; } else { m_nid.uFlags |= NIF_INFO; } // Use callback if any if (m_nid.uCallbackMessage && m_nid.hWnd) m_nid.uFlags |= NIF_MESSAGE; return Shell_NotifyIcon(msg, &m_nid); } ///////////////// // Default event handler handles right-menu and doubleclick. // Call this function from your own notification handler. // LRESULT ITrayIcon::OnTrayNotification(WPARAM wID, LPARAM lEvent) { if (wID!=m_nid.uID || (lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK)) return 0; // If there's a resource menu with the same ID as the icon, use it as // the right-button popup menu. CTrayIcon will interprets the first // item in the menu as the default command for WM_LBUTTONDBLCLK // #if 0 CMenu menu; if (!menu.LoadMenu(m_nid.uID)) return 0; CMenu* pSubMenu = menu.GetSubMenu(0); if (!pSubMenu) return 0; if (lEvent==WM_RBUTTONUP) { // Make first menu item the default (bold font) ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE); // Display the menu at the current mouse location. There's a "bug" // (Microsoft calls it a feature) in Windows 95 that requires calling // SetForegroundWindow. To find out more, search for Q135788 in MSDN. // CPoint mouse; GetCursorPos(&mouse); ::SetForegroundWindow(m_nid.hWnd); ::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0, m_nid.hWnd, NULL); } else // double click: execute first menu item ::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0); #endif return 1; // handled } // ------------------------------- [5/30/2013 Z.t] BOOL ITrayIcon::IsWow64() { BOOL bIsWow64 = FALSE; LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(_T("kernel32")),"IsWow64Process"); if (NULL != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { // handle error } } return bIsWow64; } /************************************************************************/ /* 函数:GetTrayRect,获取托盘图标区域;(有待完善,添加各种错误判断) 参数:None; 返回:返回指定托盘图标的区域; 注意:函数的功能有些太复杂,暂时没办法使用更简单的方法获取图标区域; */ /************************************************************************/ void ITrayIcon::GetTrayRect(RECT &rc) { HWND hWnd,hWndPaper; long ret; LPVOID lngAddress; // long lngTextAdr; //,lngHwndAdr,lngHwnd,lngButtonID; TCHAR strBuff[1024]={0}; TBBUTTON btnData={0}; hWnd = FindWindow(_T("Shell_TrayWnd"), NULL); // 获取任务栏句柄 [5/31/2013 Z.t] hWnd = FindWindowEx(hWnd, 0, _T("TrayNotifyWnd"), NULL); // 获取托盘区域句柄 [5/31/2013 Z.t] hWndPaper = FindWindowEx(hWnd, 0, _T("SysPager"), NULL); // 获取系统页句柄 [5/31/2013 Z.t] if(!hWndPaper) hWnd = FindWindowEx(hWnd, 0, _T("ToolbarWindow32"), NULL); else hWnd = FindWindowEx(hWndPaper, 0, _T("ToolbarWindow32"), NULL); DWORD dwProcessId = 0; GetWindowThreadProcessId(hWnd, &dwProcessId);//LOG4C_NO_FILENUM((LOG_NOTICE,"进程ID%d",dwProcessId));进程其实就是explorer.exe HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE,0,dwProcessId); if ( hProcess == NULL ) { //LOG4C_NO_FILENUM((LOG_NOTICE,"hProcess == NULL--")); return ; } lngAddress = VirtualAllocEx(hProcess,0, 0x4096, MEM_COMMIT, PAGE_READWRITE); if( lngAddress == NULL) { //LOG4C_NO_FILENUM((LOG_NOTICE,"lngAddress == NULL--")); return; } DWORD lTextAdr = 0; BYTE buff[1024] = {0}; CString strFilePath; CString strTile; HWND hMainWnd = NULL; int nDataOffset = sizeof(TBBUTTON) - sizeof(INT_PTR) - sizeof(DWORD_PTR); int nStrOffset = 18; if ( IsWow64() ) { nDataOffset+=4; nStrOffset+=6; } // 获取托盘区域的所有图标数量 [5/31/2013 Z.t] int ibtnCount = SendMessage(hWnd, TB_BUTTONCOUNT, 0, 0); LPVOID lngRect = VirtualAllocEx(hProcess,0,sizeof(RECT), MEM_COMMIT, PAGE_READWRITE); CRect rect; for(int i=0 ;i< ibtnCount;i++) { int j = i; ret = SendMessage(hWnd,TB_GETBUTTON,j, (LPARAM)(lngAddress)); // 读文本地址; ret = ReadProcessMemory(hProcess, LPVOID(long(lngAddress) + nDataOffset),&lTextAdr,4,0); if(lTextAdr != -1) { // 读文本; ret = ReadProcessMemory(hProcess, LPVOID(lTextAdr),buff,1024,0); hMainWnd = (HWND)(*((DWORD*)buff)); strFilePath = (WCHAR *)buff + nStrOffset; // 获取托盘图标进程路径 [5/31/2013 Z.t] strTile = (WCHAR *)buff + nStrOffset + MAX_PATH; // 获取托盘图标tip标题 [5/31/2013 Z.t] //_tprintf(_T("%s %s\n"),strTile,strFilePath); if (strTile.Compare(m_nid.szTip) == 0) { ::SendMessage(hWnd,TB_GETITEMRECT,(WPARAM)j,(LPARAM)lngRect); ReadProcessMemory(hProcess,lngRect,&rc, sizeof(rc),0); // 获取托盘图标区域; CWnd::FromHandle(hWnd)->ClientToScreen(&rc); } } } VirtualFreeEx( hProcess, lngAddress, 0x4096, MEM_DECOMMIT); VirtualFreeEx( hProcess, lngAddress, 0, MEM_RELEASE); VirtualFreeEx( hProcess, lngRect, sizeof(RECT), MEM_DECOMMIT); VirtualFreeEx( hProcess, lngRect, 0, MEM_RELEASE); CloseHandle(hProcess); } int ITrayIcon::OpendThread() { //LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon")); m_hThreadCtrl = CreateEvent(NULL,TRUE,FALSE,NULL); // 无信号事件; if ( m_hThreadCtrl == NULL ) { //LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon事件失败")); return -1; } m_hThreadObj = CreateThread(NULL,0,FlashingThread,this,CREATE_SUSPENDED,&m_dwThreadID); if ( m_hThreadObj == NULL ) { //LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon线程失败")); return -1; } bSuspending = true; return 0; } void ITrayIcon::TerminalThread() { if ( m_hThreadCtrl ) { SetEvent( m_hThreadCtrl ); } if (WaitForSingleObject(m_hThreadCtrl,INFINITE) != WAIT_TIMEOUT) { CloseHandle(m_hThreadObj); m_hThreadObj = NULL; } CloseHandle( m_hThreadCtrl ); m_hThreadCtrl = NULL; bSuspending = false; } BOOL ITrayIcon::StartFlashing() { if ( !bSuspending ) return FALSE; if(::ResumeThread(m_hThreadObj) == 0xFFFFFFFF) return FALSE; bSuspending = false; SetInfo(_T("环境监控系统产生新警报,请及时处理!"),_T("警报提示")); return TRUE; } BOOL ITrayIcon::StopFlashing() { if ( bSuspending ) return FALSE; if(::SuspendThread(m_hThreadObj) == 0xFFFFFFFF) return FALSE; bSuspending = true; SetIcon(_utIcon[0]); return TRUE; } /************************************************************************/ /* 线程函数:闪烁线程; */ /************************************************************************/ DWORD WINAPI ITrayIcon::FlashingThread(LPVOID lpVoid) { ITrayIcon *pTrayIcon = (ITrayIcon*)lpVoid; CPoint pt; CRect IconRect; bool bChange = FALSE; do { if ( bChange) { pTrayIcon->SetIcon(pTrayIcon->_utIcon[1]); bChange = FALSE; } else { pTrayIcon->SetIcon(pTrayIcon->_utIcon[2]); bChange = TRUE; } if( pTrayIcon->bShowMinibox ) { GetCursorPos(&pt); pTrayIcon->GetTrayRect(IconRect); if ( IconRect.PtInRect( pt) ) ::PostMessage(pTrayIcon->m_nid.hWnd,MYWM_SHOWMINBOX,1,0); else ::PostMessage(pTrayIcon->m_nid.hWnd,MYWM_SHOWMINBOX,0,0); } }while (WaitForSingleObject(pTrayIcon->m_hThreadCtrl,300) == WAIT_TIMEOUT); return 0; }