Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-5078

Windows: QSystemTrayIcon findIconGeometry on x64 bit systems

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P3: Somewhat important
    • None
    • 4.5.3
    • None

    Description

      Steps to reproduce / test case:

      If you run 32 bit compiled QT application on any 64 bit windows which uses QSystemTrayIcon and want to find out actual tray icon geometry and position, you will end up with QRect (0,0,0,0).

      That's because there is an error in:
      QRect QSystemTrayIconSys::findIconGeometry(const int iconId) in file qsystemtrayicon_win.cpp

      while doing operation :
      if(!ReadProcessMemory(trayProcess, data, &buttonData, sizeof(TBBUTTON), &numBytes))

      in 32 bit compiled application the structure of TBBUTTON differs from structure which is compiled in system's start menu bar (which is 64bit). so while reading 64 bit process memory ( start menu bar) from 32bit application (our application) you'll fill the structure with bad data because of different offsets.

      that's because the structure definition is architecture dependent :
      (copy of source from commctrl.h)

      typedef struct _TBBUTTON { 
      int iBitmap; 
      int idCommand; 
      BYTE fsState; 
      BYTE fsStyle; 
      #ifdef _WIN64 
      BYTE bReserved[6]; // padding for alignment 
      #elif defined(_WIN32) 
      BYTE bReserved[2]; // padding for alignment 
      #endif 
      DWORD_PTR dwData; 
      INT_PTR iString; 
      } TBBUTTON, NEAR* PTBBUTTON, *LPTBBUTTON; 
      typedef const TBBUTTON *LPCTBBUTTON; 
      

      the solution for this one is detecting if we are running on 64 bit or 32 bit system and then use proper structure for process memory reading.

      More information:

      here is my version of this method - tested and working.

      typedef struct _TBBUTTONX32 { 
      int iBitmap; 
      int idCommand; 
      BYTE fsState; 
      BYTE fsStyle; 
      BYTE bReserved[2]; // padding for alignment 
      DWORD_PTR dwData; 
      INT_PTR iString; 
      } TBBUTTONX32; 
      
      typedef struct _TBBUTTONX64 { 
      int iBitmap; 
      int idCommand; 
      BYTE fsState; 
      BYTE fsStyle; 
      BYTE bReserved[6]; // padding for alignment 
      DWORD_PTR dwData; 
      INT_PTR iString; 
      } TBBUTTONX64; 
      /* 
      * This function tries to determine the icon geometry from the tray 
      * 
      * If it fails an invalid rect is returned. 
      */ 
      QRect QSystemTrayIconSys::findIconGeometry(const int iconId) 
      { 
      
      #if !defined(Q_OS_WINCE) 
      SYSTEM_INFO si; 
      bool is64bit = false; 
      
      ZeroMemory(&si, sizeof(si)); 
      GetNativeSystemInfo(&si); 
      // wProcessorArchitecture field is Windows processor architecture - for sure :) 
      if((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) || 
      (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)) 
      { 
      is64bit = true; 
      } 
      
      QRect ret; 
      #endif 
      TBBUTTON 
      #if defined(Q_OS_WINCE) 
      TBBUTTON buttonData; 
      #else 
      TBBUTTONX32 buttonData32; 
      TBBUTTONX64 buttonData64; 
      #endif 
      DWORD processID = 0; 
      #if defined(Q_OS_WINCE) 
      HWND trayHandle = FindWindowW(L"Shell_TrayWnd", NULL); 
      #else 
      HWND trayHandle = FindWindowA("Shell_TrayWnd", NULL); 
      #endif 
      
      //find the toolbar used in the notification area 
      if (trayHandle) { 
      #if defined(Q_OS_WINCE) 
      trayHandle = FindWindowW(L"TrayNotifyWnd", NULL); 
      #else 
      trayHandle = FindWindowExA(trayHandle, NULL, "TrayNotifyWnd", NULL); 
      #endif 
      if (trayHandle) { 
      #if defined(Q_OS_WINCE) 
      HWND hwnd = FindWindowW(L"SysPager", NULL); 
      #else 
      HWND hwnd = FindWindowExW(trayHandle, NULL, L"SysPager", NULL); 
      #endif 
      if (hwnd) { 
      #if defined(Q_OS_WINCE) 
      hwnd = FindWindow(L"ToolbarWindow32", NULL); 
      #else 
      hwnd = FindWindowExW(hwnd, NULL, L"ToolbarWindow32", NULL); 
      #endif 
      if (hwnd) 
      trayHandle = hwnd; 
      } 
      } 
      } 
      
      if (!trayHandle) 
      return ret; 
      
      GetWindowThreadProcessId(trayHandle, &processID); 
      if (processID <= 0) 
      return ret; 
      
      HANDLE trayProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ, 0, processID); 
      if (!trayProcess) 
      return ret; 
      
      int buttonCount = SendMessage(trayHandle, TB_BUTTONCOUNT, 0, 0); 
      #if defined(Q_OS_WINCE) 
      LPVOID data = VirtualAlloc(NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE); 
      #else 
      LPVOID data; 
      if (is64bit) { 
      data = VirtualAllocEx(trayProcess, NULL, sizeof(TBBUTTONX32), MEM_COMMIT, PAGE_READWRITE); 
      } else { 
      data = VirtualAllocEx(trayProcess, NULL, sizeof(TBBUTTONX64), MEM_COMMIT, PAGE_READWRITE); 
      } 
      #endif 
      
      if ( buttonCount < 1 || !data ) { 
      CloseHandle(trayProcess); 
      return ret; 
      } 
      
      //search for our icon among all toolbar buttons 
      for (int toolbarButton = 0; toolbarButton < buttonCount; ++toolbarButton ) { 
      SIZE_T numBytes = 0; 
      DWORD appData[2] = { 0, 0 }; 
      SendMessage(trayHandle, TB_GETBUTTON, toolbarButton , (LPARAM)data); 
      #if defined(Q_OS_WINCE) 
      if(!ReadProcessMemory(trayProcess, data, &buttonData, sizeof(TBBUTTON), &numBytes)) 
      continue; 
      #else 
      if(is64bit) { 
      if(!ReadProcessMemory(trayProcess, data, &buttonData64, sizeof(buttonData64), &numBytes)) 
      continue; 
      } else { 
      if(!ReadProcessMemory(trayProcess, data, &buttonData32, sizeof(buttonData32), &numBytes)) 
      continue; 
      } 
      #endif 
      
      #if defined(Q_OS_WINCE) 
      if(!ReadProcessMemory(trayProcess, (LPVOID) buttonData.dwData, appData, sizeof(appData), &numBytes)) 
      continue; 
      #else 
      if(is64bit) { 
      if(!ReadProcessMemory(trayProcess, (LPVOID) buttonData64.dwData, appData, sizeof(appData), &numBytes)) 
      continue; 
      } else { 
      if(!ReadProcessMemory(trayProcess, (LPVOID) buttonData32.dwData, appData, sizeof(appData), &numBytes)) 
      continue; 
      } 
      #endif 
      
      
      int currentIconId = appData[1]; 
      HWND currentIconHandle = (HWND) appData[0]; 
      
      #if defined(Q_OS_WINCE) 
      bool isHidden = buttonData.fsState & TBSTATE_HIDDEN; 
      #else 
      bool isHidden; 
      if(is64bit) { 
      isHidden = buttonData64.fsState & TBSTATE_HIDDEN; 
      } else { 
      isHidden = buttonData32.fsState & TBSTATE_HIDDEN; 
      } 
      #endif 
      
      if (currentIconHandle == winId() && 
      currentIconId == iconId && !isHidden) { 
      SendMessage(trayHandle, TB_GETITEMRECT, toolbarButton , (LPARAM)data); 
      RECT iconRect = {0, 0}; 
      if(ReadProcessMemory(trayProcess, data, &iconRect, sizeof(RECT), &numBytes)) { 
      MapWindowPoints(trayHandle, NULL, (LPPOINT)&iconRect, 2); 
      QRect geometry(iconRect.left + 1, iconRect.top + 1, 
      iconRect.right - iconRect.left - 2, 
      iconRect.bottom - iconRect.top - 2); 
      if (geometry.isValid()) 
      ret = geometry; 
      break; 
      } 
      } 
      } 
      #if defined(Q_OS_WINCE) 
      VirtualFree(data, 0, MEM_RELEASE); 
      #else 
      VirtualFreeEx(trayProcess, data, 0, MEM_RELEASE); 
      #endif 
      CloseHandle(trayProcess); 
      return ret; 
      } 
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            Unassigned Unassigned
            sthomass Stian Sandvik Thomassen (closed Nokia identity) (Inactive)
            Votes:
            3 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes