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; }