// AppSearch // // Tool for searching a user's hard drives and locating // applications that can be patched // // Author: t-michkr (9 June 2000) // // AppSearch.cpp // User-interface // We make use of some Win2K/IE5 common controls #include #include #include #include #include #include #include #include "main.h" #include "searchdb.h" #include "filebrowser.h" #include "resource.h" // Compare data, used for sorting listview items. struct SCompareParam { HWND hwList; int iSubItem; }; // Size of some static controls const int c_nStaticLineHeight = 2; const int c_nResultFrameWidth = 2; // Minimum height of the result window const int c_nResultListHeight = 201; // ID's of controls we create ourselves const int c_iStaticLineID = 50; const int c_iResultListID = 51; const int c_iResultFrameID = 52; const int c_iStatusBarID = 53; const int c_iFindAnimID = 54; // Positions of menu items (these will be needed to be changed // if the menu is altered) const int c_iViewMenuPos = 2; const int c_iArrangeIconsPos = 5; // List view column info const int c_nListColumnWidth = 250; const int c_nNumListColumns = 2; const int c_iListColumnNameIDS[c_nNumListColumns] = { IDS_LISTNAME, IDS_LISTPATH}; // Number of children that need to be adjusted on resize const int c_nChildren = 6; // ID's of children that need to be adjusted on resize int g_aiChildrenIDs[c_nChildren] = {IDC_BROWSE, IDC_FINDNOW, IDC_STOP, IDC_CLEARALL, IDC_DRIVELIST, c_iFindAnimID}; // Margins of those children, determined from dialog box in HandleInitDialog(). int g_aiChildrenMargins[c_nChildren]; // Minimum window width and window height, determined from dialog box // in HandleInitDialog(). int g_nMinWindowWidth; int g_nWindowHeight; // Whether or not the user is browsing through menus, used for displaying // menu help text. BOOL g_fInMenu = FALSE; // Instance of this app. HINSTANCE g_hinst = 0; // Original window proc for the list view control. WNDPROC pfnLVOrgWndProc = 0; // Subclassed window proc for list view, intercepts WM_PAINT messages // and writes "No items in view" if empty. LRESULT CALLBACK LVWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam); // Dialog proc for main application dialog. BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam); // Display shimming information for specified exe. void ShowShimInfo(HWND hwnd, TCHAR* szAppPath); // Returns an allocated string with resource ID id, or 0 on failure. TCHAR* LoadStringResource(UINT id); // Print an error box with message in string resource uiMsg. void Error(HWND hwnd, UINT uiMsg); // Compare entries in the listview, used for sorting. int CALLBACK CompareListEntries(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); // Start searching the AppCompat database. void StartSearch(HWND hwnd); // Terminate the current search, does nothing if no search is active. void StopSearch(HWND hwnd); // Remove all results shown void ClearResults(HWND hwnd); // Update the status bar with the new message. void UpdateStatus(HWND hwnd, PTSTR szMsg); // Add a new app to the list void AddApp(SMatchedExe* pme, HWND hwList); // Message Handlers. BOOL HandleInitDialog(HWND hwnd); void HandleBrowse(HWND hwnd); void HandleCommand(HWND hwnd, int iCtrlID, HWND hwChild); void HandleEnterMenuLoop(HWND hwnd); void HandleExitMenuLoop(HWND hwnd); void HandleGetMinMaxInfo(HWND hwnd, LPMINMAXINFO pmmi); void HandleMenuSelect(HWND hwnd, HMENU hMenu, UINT uiMenuID, UINT uiFlags); void HandleNotify(HWND hwnd, int iCtrlID, void* pvArg); void HandleSearchAddApp(HWND hwnd); void HandleSearchUpdate(HWND hwnd, PTSTR szMsg); void HandleSize(HWND hwnd, int iWidth, int iHeight); void HandleSizing(HWND hwnd, int iEdge, LPRECT pRect); // Program entry point. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int) { // Save our global instance handle. g_hinst = hInstance; // Make sure the common controls DLL is loaded. INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); // We use the list view, status bar, tree control, animate, // tooltip, and comboboxex classes icc.dwICC = ICC_USEREX_CLASSES | ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES | ICC_TREEVIEW_CLASSES | ICC_ANIMATE_CLASS | ICC_TAB_CLASSES ; if(InitCommonControlsEx(&icc) == FALSE) return 0; // Run the actual dialog for the application return DialogBox(g_hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), 0, DialogProc); } // Dialog proc for main dialog. BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { switch(uiMsg) { // Do basic initialization, return FALSE means do not continue // creating the window. case WM_INITDIALOG: return HandleInitDialog(hwndDlg); break; // Control sizing, to maintain a minimum dialog size. case WM_SIZE: HandleSize(hwndDlg, LOWORD(lParam), HIWORD(lParam)); break; // Control sizing, to maintain a minimum dialog box size. case WM_SIZING: HandleSizing(hwndDlg, wParam, reinterpret_cast(lParam)); break; // Control maximizing, to maintain dialog size. case WM_GETMINMAXINFO: HandleGetMinMaxInfo(hwndDlg, reinterpret_cast(lParam)); break; // Handle child control messages case WM_COMMAND: HandleCommand(hwndDlg, LOWORD(wParam), reinterpret_cast(lParam)); break; // Handle notifications from child controls case WM_NOTIFY: HandleNotify(hwndDlg, static_cast(wParam), reinterpret_cast(lParam)); break; // Print help messages when user goes over a menu item. case WM_MENUSELECT: HandleMenuSelect(hwndDlg, reinterpret_cast(lParam), LOWORD(wParam), HIWORD(wParam)); break; // Note when user starts browsing through a menu. case WM_ENTERMENULOOP: HandleEnterMenuLoop(hwndDlg); break; // Note when user exits a menu. case WM_EXITMENULOOP: HandleExitMenuLoop(hwndDlg); break; // Search thread just found another app. case WM_SEARCHDB_ADDAPP: HandleSearchAddApp(hwndDlg); break; // Search thread is looking through a new directory case WM_SEARCHDB_UPDATE: HandleSearchUpdate(hwndDlg, reinterpret_cast(lParam)); break; // Search thread is complete. case WM_SEARCHDB_DONE: // Cleanup any leftover apps (just in case this message // is received before WM_SEARCHDB_ADDAPP) HandleSearchAddApp(hwndDlg); // Terminate search StopSearch(hwndDlg); break; // User wants to close this dialog case WM_CLOSE: // Terminate search StopSearch(hwndDlg); EndDialog(hwndDlg, TRUE); break; default: return FALSE; } return TRUE; } // Do basic dialog box initialization. BOOL HandleInitDialog(HWND hwnd) { // Change the application icon HICON hIcon; hIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_APP)); if(!hIcon) return FALSE; // For SetClassLongPtr(), a return value of zero may not be an // error, if the previous value was not set. Setting the // last error to 0 and checking for that value indicates // success. // BUGBUG // In Whistler, SetClassLongPtr returns an error, but still // sets the correct icon. No error is returned for Win2K. SetClassLongPtr(hwnd, GCLP_HICON, reinterpret_cast(hIcon)); SetClassLongPtr(hwnd, GCLP_HICONSM, reinterpret_cast(hIcon)); // Get minimum dimensions of dialog RECT rcWindow; if(!GetWindowRect(hwnd, &rcWindow)) return FALSE; g_nMinWindowWidth = rcWindow.right - rcWindow.left; g_nWindowHeight = rcWindow.bottom - rcWindow.top; // Add a static line across the top (We can't put this in the resource template) RECT rectCl; if(!GetClientRect(hwnd, &rectCl)) return FALSE; HWND hwStaticLine = CreateWindow(TEXT("static"),TEXT(""), SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, rectCl.top, rectCl.left, rectCl.right - rectCl.left, c_nStaticLineHeight, hwnd, reinterpret_cast(c_iStaticLineID), g_hinst, 0); if(!hwStaticLine) return FALSE; // Fill the combobox. HWND hwComboBox = GetDlgItem(hwnd, IDC_DRIVELIST); if(!hwComboBox) return FALSE; SHFILEINFO sfi; HIMAGELIST himagelist = reinterpret_cast(SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON)); LRESULT lr = SendMessage(hwComboBox, CBEM_SETIMAGELIST, 0, reinterpret_cast(himagelist)); assert(lr == NULL); COMBOBOXEXITEM cbitem; ZeroMemory(&cbitem, sizeof(cbitem)); cbitem.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; cbitem.iItem = -1; cbitem.pszText = LoadStringResource(IDS_ALLDRIVES); if(!cbitem.pszText) return FALSE; cbitem.cchTextMax = lstrlen(cbitem.pszText) + 1; cbitem.iIndent = 0; LPITEMIDLIST pidl; SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, &pidl); SHGetFileInfo(reinterpret_cast(pidl), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_PIDL); cbitem.iImage = sfi.iIcon; cbitem.iSelectedImage = sfi.iIcon; if(SendMessage(hwComboBox, CBEM_INSERTITEM, 0, reinterpret_cast(&cbitem))) { delete cbitem.pszText; return FALSE; } delete cbitem.pszText; LPMALLOC pMalloc; SHGetMalloc(&pMalloc); pMalloc->Free(pidl); pMalloc->Release(); TCHAR* szDrives = 0; DWORD dwLen = GetLogicalDriveStrings(0, 0); if(dwLen != 0) { szDrives = new TCHAR[dwLen+1]; if(!szDrives) { Error(hwnd, IDS_NOMEMSTOPPROG); return FALSE; } } else return FALSE; if(!GetLogicalDriveStrings(dwLen, szDrives)) { delete szDrives; return FALSE; } TCHAR* szCurrDrive = szDrives; while(*szCurrDrive) { if(GetDriveType(szCurrDrive)==DRIVE_FIXED) { SHGetFileInfo(szCurrDrive, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX); cbitem.pszText = szCurrDrive; cbitem.cchTextMax = lstrlen(cbitem.pszText) + 1; cbitem.iIndent = 1; cbitem.iImage = sfi.iIcon; cbitem.iSelectedImage = sfi.iIcon; SendMessage(hwComboBox, CBEM_INSERTITEM, 0, reinterpret_cast(&cbitem)); } szCurrDrive += lstrlen(szCurrDrive)+1; } delete szDrives; if(SendMessage(hwComboBox, CB_SETCURSEL, 0, 0)== -1) return FALSE; // Add the animation control. HWND hwChild; RECT rcChild; POINT pt; HWND hwAnim = Animate_Create(hwnd, c_iFindAnimID, WS_CHILD | ACS_CENTER | ACS_TRANSPARENT, g_hinst); hwChild = GetDlgItem(hwnd, IDC_ANIMSPACEHOLDER); GetWindowRect(hwChild, &rcChild); DestroyWindow(hwChild); pt.x = rcChild.left; pt.y = rcChild.top; ScreenToClient(hwnd, &pt); SetWindowPos(hwAnim, 0, pt.x, pt.y, rcChild.right-rcChild.left, rcChild.bottom - rcChild.top, SWP_NOZORDER); Animate_Open(hwAnim, MAKEINTRESOURCE(IDR_FINDAVI)); ShowWindow(hwAnim, SW_SHOW); // Get margins of all child window controls pt.x = 0; pt.y = 0; for(int i = 0; i < c_nChildren; i++) { hwChild = GetDlgItem(hwnd, g_aiChildrenIDs[i]); if(!hwChild) return FALSE; if(!GetWindowRect(hwChild, &rcChild)) return FALSE; pt.x = rcChild.right; if(!ScreenToClient(hwnd, &pt)) return FALSE; g_aiChildrenMargins[i] = rectCl.right - pt.x; } // Setup default checked state HMENU hMenu = GetMenu(hwnd); if(!hMenu) return FALSE; CheckMenuRadioItem(hMenu, ID_VIEW_LARGEICONS, ID_VIEW_ASDETAILS, ID_VIEW_ASDETAILS, MF_BYCOMMAND); if(InitSearchDB() == FALSE) return FALSE; return TRUE; } // Display a browse dialog box, so that the user can select // a specific path to search in. void HandleBrowse(HWND hwnd) { // Show the browse dialog box, and get user response. TCHAR* szPath = BrowseForFolder(hwnd, 0, BF_SELECTDIRECTORIES | BF_HARDDRIVES); // If they didn't select cancel . . . if(szPath) { HWND hwDirSelBox = GetDlgItem(hwnd, IDC_DRIVELIST); if(!hwDirSelBox) { DestroyWindow(hwnd); return; } // For some reason, SHGetFileInfo doesn't work // properly if the drive isn't terminated with '\' if(szPath[lstrlen(szPath)-1] == TEXT(':')) lstrcat(szPath, TEXT("\\")); // Insert this item into the edit control of the combo box. SHFILEINFO sfi; COMBOBOXEXITEM cbim; cbim.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE; cbim.iItem = -1; cbim.pszText = szPath; cbim.cchTextMax = lstrlen(szPath); SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX); cbim.iImage = sfi.iIcon; cbim.iSelectedImage = sfi.iIcon; if(!SendMessage(hwDirSelBox, CBEM_SETITEM, 0, reinterpret_cast(&cbim))) { DestroyWindow(hwnd); return; } // For some reason, the icon in the edit box isn't updated // unless the box is selected, loses focus, and then regains // focus, so do that here. if(!SetFocus(hwDirSelBox)) { DestroyWindow(hwnd); return; } if(!SetFocus(GetDlgItem(hwnd, IDC_BROWSE))) { DestroyWindow(hwnd); return; } if(!SetFocus(hwDirSelBox)) { DestroyWindow(hwnd); return; } } } // Handle a command from a child input control. void HandleCommand(HWND hwnd, int iCtrlID, HWND) { HMENU hMenu; HWND hwList; DWORD dwStyle, dwExStyle; SHELLEXECUTEINFO shExecInfo; SCompareParam cp; switch(iCtrlID) { // MENU COMMANDS case ID_APPSELECT_SHOWSHIMINFO: case ID_FILE_SHOWSHIMINFO: // Get list view control hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { int nCount = ListView_GetItemCount(hwList); int i; // Loop through all items finding a selected one. for(i = 0; i < nCount; i++) { if(ListView_GetItemState(hwList, i, LVIS_SELECTED) & LVIS_SELECTED) break; } // None found, we're done. if(i == nCount) break; TCHAR szBuffer[c_nMaxStringLength]; ListView_GetItemText(hwList, i, 1, szBuffer, c_nMaxStringLength); ShowShimInfo(hwnd, szBuffer); } break; // Use selected properties case ID_APPSELECT_PROPERTIES: case ID_FILE_PROPERTIES: // Get list view control hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { int nCount = ListView_GetItemCount(hwList); int i; // Loop through all items finding a selected one. for(i = 0; i < nCount; i++) { if(ListView_GetItemState(hwList, i, LVIS_SELECTED) & LVIS_SELECTED) break; } // None found, we're done. if(i == nCount) break; TCHAR szBuffer[c_nMaxStringLength]; ListView_GetItemText(hwList, i, 1, szBuffer, c_nMaxStringLength); ZeroMemory(&shExecInfo, sizeof(shExecInfo)); shExecInfo.cbSize = sizeof(shExecInfo); shExecInfo.lpFile = szBuffer; shExecInfo.lpVerb = TEXT("properties"); shExecInfo.fMask = SEE_MASK_INVOKEIDLIST; ShellExecuteEx(&shExecInfo); } break; case ID_FILE_EXIT: EndDialog(hwnd, TRUE); break; case ID_EDIT_SELECTALL: hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { int nCount = ListView_GetItemCount(hwList); for(int i = 0; i < nCount; i++) ListView_SetItemState(hwList, i, LVIS_SELECTED, LVIS_SELECTED); } break; case ID_EDIT_INVERTSELECTION: hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { int nCount = ListView_GetItemCount(hwList); for(int i = 0; i < nCount; i++) ListView_SetItemState(hwList, i, LVIS_SELECTED ^ ListView_GetItemState(hwList, i, LVIS_SELECTED), LVIS_SELECTED); } break; case ID_VIEW_LARGEICONS: case ID_VIEW_SMALLICONS: case ID_VIEW_ASLIST: case ID_VIEW_ASDETAILS: hMenu = GetMenu(hwnd); CheckMenuRadioItem(hMenu, ID_VIEW_LARGEICONS, ID_VIEW_ASDETAILS, iCtrlID, MF_BYCOMMAND); dwStyle = WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS; dwExStyle = LVS_EX_LABELTIP; switch(iCtrlID) { case ID_VIEW_LARGEICONS: dwStyle |= LVS_ICON; break; case ID_VIEW_SMALLICONS: dwStyle |= LVS_SMALLICON; break; case ID_VIEW_ASLIST: dwStyle |= LVS_LIST; break; case ID_VIEW_ASDETAILS: dwStyle |= LVS_REPORT; dwExStyle |= LVS_EX_FULLROWSELECT; break; } hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { SetWindowLongPtr(hwList, GWL_STYLE, dwStyle); ListView_SetExtendedListViewStyleEx(hwList, 0, dwExStyle); } break; case ID_VIEW_ARRANGEICONS_BYNAME: hwList = GetDlgItem(hwnd, c_iResultListID); cp.hwList = hwList; cp.iSubItem = 0; if(hwList) ListView_SortItemsEx(hwList, CompareListEntries, &cp); break; case ID_VIEW_ARRANGEICONS_BYPATH: hwList = GetDlgItem(hwnd, c_iResultListID); cp.hwList = hwList; cp.iSubItem = 1; if(hwList) ListView_SortItemsEx(hwList, CompareListEntries, &cp); break; case ID_VIEW_CHOOSECOLUMNS: break; case ID_VIEW_REFRESH: StartSearch(hwnd); break; // PUSH BUTTON COMMANDS case IDC_BROWSE: HandleBrowse(hwnd); break; case IDC_FINDNOW: StartSearch(hwnd); break; case IDC_STOP: StopSearch(hwnd); break; case IDC_CLEARALL: ClearResults(hwnd); break; } } void HandleSize(HWND hwnd, int /*nWidth*/, int /*nHeight*/) { RECT rectCl; GetClientRect(hwnd, &rectCl); HWND hwStatic = GetDlgItem(hwnd, c_iStaticLineID); if(!hwStatic) { DestroyWindow(hwnd); return; } MoveWindow(hwStatic,rectCl.left, rectCl.top, rectCl.right-rectCl.left, c_nStaticLineHeight, TRUE); // Adjust list box and status bar at the bottom of the window HWND hwListBox, hwStatusBar, hwListFrame; hwListFrame = GetDlgItem(hwnd, c_iResultFrameID); hwListBox = GetDlgItem(hwnd, c_iResultListID); hwStatusBar= GetDlgItem(hwnd, c_iStatusBarID); assert(hwListBox == 0 ? !hwStatusBar && !hwListFrame : true); if(hwListBox && hwStatusBar && hwListFrame) { RECT rectWnd; GetWindowRect(hwnd, &rectWnd); POINT ptListTop; ptListTop.x = 0; ptListTop.y = rectWnd.top + g_nWindowHeight; ScreenToClient(hwnd, &ptListTop); RECT rectSB; GetClientRect(hwStatusBar, &rectSB); POINT ptStatusTop; ptStatusTop.x = 0; ptStatusTop.y = rectWnd.bottom - (rectSB.bottom - rectSB.top); ScreenToClient(hwnd, &ptStatusTop); MoveWindow(hwListFrame, rectCl.left, ptListTop.y, rectCl.right - rectCl.left, ptStatusTop.y - ptListTop.y, TRUE); MoveWindow(hwListBox, rectCl.left + c_nResultFrameWidth, ptListTop.y + c_nResultFrameWidth, rectCl.right - rectCl.left - 2 * c_nResultFrameWidth, ptStatusTop.y - ptListTop.y - 2 * c_nResultFrameWidth, TRUE); MoveWindow(hwStatusBar, rectCl.left, ptStatusTop.y, rectCl.right - rectCl.left, rectSB.bottom - rectSB.top, TRUE); } // Adjust all controls RECT rcChild; HWND hwChild; POINT ptTop; int iWidth, iHeight; for(int i = 0; i < c_nChildren; i++) { hwChild = GetDlgItem(hwnd, g_aiChildrenIDs[i]); if(!hwChild) { DestroyWindow(hwnd); return; } GetClientRect(hwChild, &rcChild); iWidth = rcChild.right - rcChild.left; iHeight = rcChild.bottom - rcChild.top; ptTop.x = rcChild.left; ptTop.y = rcChild.top; ClientToScreen(hwChild, &ptTop); ScreenToClient(hwnd, &ptTop); if(g_aiChildrenIDs[i] == IDC_DRIVELIST) iWidth = rectCl.right - g_aiChildrenMargins[i] - ptTop.x; if(g_aiChildrenIDs[i] == IDC_DRIVELIST) MoveWindow(hwChild, ptTop.x, ptTop.y, iWidth, iHeight, TRUE); else MoveWindow(hwChild, rectCl.right - g_aiChildrenMargins[i] - iWidth, ptTop.y, iWidth, iHeight, TRUE); } for(i = 0; i < c_nChildren; i++) InvalidateRect(GetDlgItem(hwnd, g_aiChildrenIDs[i]), 0, TRUE); } void HandleSizing(HWND hwnd, int iEdge, LPRECT pRect) { if((pRect->right - pRect->left) < g_nMinWindowWidth) { if(iEdge == WMSZ_BOTTOMLEFT || iEdge == WMSZ_LEFT || iEdge == WMSZ_TOPLEFT) { pRect->left = pRect->right - g_nMinWindowWidth; } else if(iEdge == WMSZ_BOTTOMRIGHT || iEdge == WMSZ_RIGHT || iEdge == WMSZ_TOPRIGHT) { pRect->right = pRect->left + g_nMinWindowWidth; } // Should never get here else { OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n")); } } if(GetDlgItem(hwnd, c_iResultListID)) { HWND hwStatusBar = GetDlgItem(hwnd, c_iStatusBarID); RECT rectStatus; GetClientRect(hwStatusBar, &rectStatus); if((pRect->bottom - pRect->top) < (g_nWindowHeight + c_nResultListHeight)) { if(iEdge == WMSZ_BOTTOM || iEdge == WMSZ_BOTTOMLEFT || iEdge == WMSZ_BOTTOMRIGHT) { pRect->bottom = pRect->top + (g_nWindowHeight + c_nResultListHeight); } else if((iEdge == WMSZ_TOP) || (iEdge == WMSZ_TOPLEFT) || (iEdge == WMSZ_TOPRIGHT)) { pRect->top = pRect->bottom - (g_nWindowHeight + c_nResultListHeight); } // Should never get here else { OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n")); } } } else { if((pRect->bottom - pRect->top) != g_nWindowHeight) { if(iEdge == WMSZ_BOTTOM || iEdge == WMSZ_BOTTOMLEFT || iEdge == WMSZ_BOTTOMRIGHT) { pRect->bottom = pRect->top + g_nWindowHeight; } else if((iEdge == WMSZ_TOP) || (iEdge == WMSZ_TOPLEFT) || (iEdge == WMSZ_TOPRIGHT)) { pRect->top = pRect->bottom - g_nWindowHeight; } // Should never get here else { OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n")); } } } } void HandleGetMinMaxInfo(HWND hwnd, LPMINMAXINFO pmmi) { // Maximize normally if we have the result list box if(GetDlgItem(hwnd, c_iResultListID)) return; pmmi->ptMaxSize.y = g_nWindowHeight; } void HandleNotify(HWND hwnd, int iCtrlID, void* pvArg) { LPNMHDR pHdr = reinterpret_cast(pvArg); LPNMLISTVIEW pnmlvItem = reinterpret_cast(pvArg); HWND hwList; HMENU hMenu; switch(iCtrlID) { case c_iResultListID: hwList = GetDlgItem(hwnd, c_iResultListID); switch(pHdr->code) { case NM_RCLICK: // Check if the click is on any row if(pnmlvItem->iItem != -1) { // Create a context sensitive menu. HMENU hmContext = LoadMenu(g_hinst, MAKEINTRESOURCE(IDM_APPSELECT)); hmContext = GetSubMenu(hmContext, 0); ClientToScreen(GetDlgItem(hwnd, c_iResultListID), &pnmlvItem->ptAction); TrackPopupMenuEx(hmContext, 0, pnmlvItem->ptAction.x, pnmlvItem->ptAction.y, hwnd, 0); } else { // FIXME // Create the shortcut menu } break; case LVN_ITEMCHANGED: hwList = GetDlgItem(hwnd, c_iResultListID); if(hwList) { int nCount = ListView_GetItemCount(hwList); int i; for(i = 0; i < nCount; i++) { if(ListView_GetItemState(hwList, i, LVIS_SELECTED) & LVIS_SELECTED) break; } hMenu = GetMenu(hwnd); if(i == nCount) { EnableMenuItem(hMenu, ID_FILE_SHOWSHIMINFO, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, ID_FILE_PROPERTIES, MF_BYCOMMAND | MF_GRAYED); } else { EnableMenuItem(hMenu, ID_FILE_SHOWSHIMINFO, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, ID_FILE_PROPERTIES, MF_BYCOMMAND | MF_ENABLED); } } break; } default: break; } } void HandleMenuSelect(HWND hwnd, HMENU hMenu, UINT uiMenuID, UINT uiFlags) { HWND hwStatusBar = GetDlgItem(hwnd,c_iStatusBarID); if(!hwStatusBar) return; TCHAR* szText = 0; if(uiFlags & MF_POPUP) { if(hMenu != GetMenu(hwnd)) { // Not a top-level menu // Go through all lower-level submenus // Right now only submenu is Arrange Icons szText = LoadStringResource(IDS_VIEW_ARRANGEICONS); } } else { switch(uiMenuID) { case ID_FILE_SHOWSHIMINFO: szText = LoadStringResource(IDS_FILE_SHOWSHIM); break; case ID_FILE_DOWNLOADPATCH: szText = LoadStringResource(IDS_FILE_DOWNLOADPATCH); break; case ID_FILE_PROPERTIES: szText = LoadStringResource(IDS_FILE_PROPERTIES); break; case ID_FILE_SAVESEARCH: szText = LoadStringResource(IDS_FILE_SAVESEARCH); break; case ID_FILE_EXIT: szText = LoadStringResource(IDS_FILE_EXIT); break; case ID_EDIT_SELECTALL: szText = LoadStringResource(IDS_EDIT_SELECTALL); break; case ID_EDIT_INVERTSELECTION: szText = LoadStringResource(IDS_EDIT_INVERTSELECTION); break; case ID_VIEW_LARGEICONS: szText = LoadStringResource(IDS_VIEW_LARGEICONS); break; case ID_VIEW_SMALLICONS: szText = LoadStringResource(IDS_VIEW_SMALLICONS); break; case ID_VIEW_ASLIST: szText = LoadStringResource(IDS_VIEW_ASLIST); break; case ID_VIEW_ASDETAILS: szText = LoadStringResource(IDS_VIEW_ASDETAILS); break; case ID_VIEW_ARRANGEICONS_BYNAME: szText = LoadStringResource(IDS_VIEW_ARRANGEICONS_BYNAME); break; case ID_VIEW_ARRANGEICONS_BYPATH: szText = LoadStringResource(IDS_VIEW_ARRANGEICONS_BYPATH); break; case ID_VIEW_CHOOSECOLUMNS: szText = LoadStringResource(IDS_VIEW_CHOOSECOLUMNS); break; case ID_VIEW_REFRESH: szText = LoadStringResource(IDS_VIEW_REFRESH); break; case ID_HELP_HELPTOPICS: szText = LoadStringResource(IDS_HELP_HELPTOPICS); break; case ID_HELP_WHATSTHIS: szText = LoadStringResource(IDS_HELP_WHATSTHIS); break; } } if(szText) { SetWindowText(hwStatusBar, szText); delete szText; } else SetWindowText(hwStatusBar, TEXT("")); } void HandleEnterMenuLoop(HWND hwnd) { UpdateStatus(hwnd, TEXT("")); g_fInMenu = TRUE; } void HandleExitMenuLoop(HWND hwnd) { UpdateStatus(hwnd, LoadStringResource(IDS_SEARCHDONE)); g_fInMenu = FALSE; } void HandleSearchAddApp(HWND hwnd) { HWND hwList = GetDlgItem(hwnd, c_iResultListID); if(!hwList) return; SMatchedExe* pme; while( (pme = GetMatchedExe()) != 0) AddApp(pme, hwList); delete pme; } void HandleSearchUpdate(HWND hwnd, PTSTR szMsg) { if(g_fInMenu == FALSE) UpdateStatus(hwnd, szMsg); delete szMsg; } int CALLBACK CompareListEntries(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { SCompareParam* pCompParam = reinterpret_cast(lParamSort); TCHAR szBuffer1[c_nMaxStringLength]; TCHAR szBuffer2[c_nMaxStringLength]; ListView_GetItemText(pCompParam->hwList, lParam1, pCompParam->iSubItem, szBuffer1, c_nMaxStringLength); ListView_GetItemText(pCompParam->hwList, lParam2, pCompParam->iSubItem, szBuffer2, c_nMaxStringLength); return lstrcmp(szBuffer1, szBuffer2); } void UpdateStatus(HWND hwnd, PTSTR szMsg) { HWND hwStatus = GetDlgItem(hwnd, c_iStatusBarID); if(!hwStatus) return; SetWindowText(hwStatus, szMsg); } void AddApp(SMatchedExe* pme, HWND hwList) { int iItem, iImage; HICON hIcon; HIMAGELIST himl = ListView_GetImageList(hwList, LVSIL_NORMAL); if(!himl) { himl = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR | ILC_MASK, 0, 0); if(!himl) { DestroyWindow(GetParent(hwList)); return; } hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION)); ImageList_AddIcon(himl, hIcon); ListView_SetImageList(hwList, himl, LVSIL_NORMAL); } HIMAGELIST himlSm = ListView_GetImageList(hwList, LVSIL_SMALL); if(!himlSm) { himlSm = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_MASK, 0, 0); if(!himlSm) { DestroyWindow(GetParent(hwList)); return; } hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION)); ImageList_AddIcon(himlSm, hIcon); ListView_SetImageList(hwList, himlSm, LVSIL_SMALL); } hIcon = ExtractIcon(g_hinst, pme->szPath, 0); if(!hIcon) { iImage = 0; } else { iImage = ImageList_AddIcon(himl, hIcon); if(iImage == -1) iImage = 0; int iImageSm = ImageList_AddIcon(himlSm, hIcon); assert(iImage == iImageSm); DestroyIcon(hIcon); } iItem = ListView_GetItemCount(hwList); // Add app to the list view. LVITEM lvItem; lvItem.mask = LVIF_IMAGE | LVIF_TEXT; lvItem.iItem = iItem; lvItem.iSubItem = 0; lvItem.pszText = pme->szAppName; lvItem.cchTextMax = lstrlen(pme->szAppName); lvItem.iImage = iImage; ListView_InsertItem(hwList, &lvItem); lvItem.mask = LVIF_TEXT; lvItem.iItem = iItem; lvItem.iSubItem = 1; lvItem.pszText = pme->szPath; lvItem.cchTextMax = lstrlen(pme->szPath); ListView_SetItem(hwList, &lvItem); // If no items were in view prior, refresh view //(otherwise "There are no items . . ." message will stay in listview. if(iItem == 0) RedrawWindow(hwList, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW); } // Start searching the database void StartSearch(HWND hwnd) { HWND hwList, hwStatus, hwResultFrame; hwList = GetDlgItem(hwnd, c_iResultListID); hwStatus = GetDlgItem(hwnd, c_iStatusBarID); hwResultFrame = GetDlgItem(hwnd, c_iResultFrameID); // Both should be NULL or both non-NULL assert( (hwList == 0) ? (hwStatus == 0 && hwResultFrame == 0) : true); if(!hwList) { // Expand the window to encompass the new lsitview. RECT rectWnd; GetWindowRect(hwnd, &rectWnd); MoveWindow(hwnd, rectWnd.left, rectWnd.top, rectWnd.right - rectWnd.left, c_nResultListHeight + g_nWindowHeight, TRUE); // See if use has checked show small icons, large icons, etc., // and set appropriate listview style. MENUITEMINFO mii; HMENU hMenu = GetMenu(hwnd); ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; DWORD dwStyle = WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS; DWORD dwExStyle = LVS_EX_LABELTIP; GetMenuItemInfo(hMenu, ID_VIEW_LARGEICONS, FALSE, &mii); if(mii.fState & MFS_CHECKED) dwStyle |= LVS_ICON; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; GetMenuItemInfo(hMenu, ID_VIEW_SMALLICONS, FALSE, &mii); if(mii.fState & MFS_CHECKED) dwStyle |= LVS_SMALLICON; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; GetMenuItemInfo(hMenu, ID_VIEW_ASLIST, FALSE, &mii); if(mii.fState & MFS_CHECKED) dwStyle |= LVS_LIST; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; GetMenuItemInfo(hMenu, ID_VIEW_ASDETAILS, FALSE, &mii); if(mii.fState & MFS_CHECKED) { dwStyle |= LVS_REPORT; dwExStyle |= LVS_EX_FULLROWSELECT; } // Create the status bar. TCHAR* szCaption = LoadStringResource(IDS_SEARCHING); if(!szCaption) { DestroyWindow(hwnd); return; } hwStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE, szCaption, hwnd, c_iStatusBarID); if(!hwStatus) { DestroyWindow(hwnd); return; } delete szCaption; RECT rectCl; RECT rectSB; GetClientRect(hwnd, &rectCl); GetClientRect(hwnd, &rectSB); // Round about way of getting top coordinate of // status bar to a client coordinate in main window POINT ptStatusTop; ptStatusTop.x = 0; ptStatusTop.y = rectSB.top; ClientToScreen(hwStatus, &ptStatusTop); ScreenToClient(hwnd, &ptStatusTop); // Bottom of window prior to resizing POINT ptListTop; ptListTop.x = 0; ptListTop.y = rectWnd.bottom; ScreenToClient(hwnd, &ptListTop); // Create static frame around listview. hwResultFrame = CreateWindowEx(0, TEXT("static"), TEXT(""), WS_VISIBLE | WS_CHILD | SS_BLACKRECT | SS_SUNKEN, rectCl.left, ptListTop.y, rectCl.right-rectCl.left, ptStatusTop.y - ptListTop.y, hwnd, reinterpret_cast(c_iResultFrameID), g_hinst, 0); if(!hwResultFrame) { DestroyWindow(hwnd); return; } // Create the listview window. hwList = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), dwStyle, rectCl.left + c_nResultFrameWidth, ptListTop.y + c_nResultFrameWidth, rectCl.right - rectCl.left - 2 * c_nResultFrameWidth, ptStatusTop.y - ptListTop.y - c_nResultFrameWidth, hwnd,reinterpret_cast(c_iResultListID), g_hinst, 0); if(!hwList) { DestroyWindow(hwnd); return; } // Subclass the window pfnLVOrgWndProc = reinterpret_cast(GetWindowLongPtr(hwList, GWL_WNDPROC)); SetWindowLongPtr(hwList, GWL_WNDPROC, reinterpret_cast(LVWndProc)); ListView_SetExtendedListViewStyleEx(hwList, 0, dwExStyle); // Insert listview columns. for(int i = 0; i < c_nNumListColumns; i++) { LVCOLUMN lvCol; lvCol.mask = LVCF_FMT | LVCF_TEXT | LVCF_ORDER | LVCF_WIDTH | LVCF_SUBITEM; lvCol.fmt = LVCFMT_LEFT; lvCol.pszText = LoadStringResource(c_iListColumnNameIDS[i]); lvCol.cchTextMax = lstrlen(lvCol.pszText); lvCol.iSubItem = i; lvCol.iOrder = i; lvCol.cx = c_nListColumnWidth; ListView_InsertColumn(hwList, i, &lvCol); delete lvCol.pszText; } // "Remaximize" if it is already maximized if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_MAXIMIZE) { HWND hwDesktop = GetDesktopWindow(); RECT rcDesktop; GetClientRect(hwDesktop, &rcDesktop); MoveWindow(hwnd, rcDesktop.left, rcDesktop.top, rcDesktop.right - rcDesktop.left, rcDesktop.bottom - rcDesktop.top, TRUE); // Set correct restore position WINDOWPLACEMENT wndpl; GetWindowPlacement(hwnd, &wndpl); wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + g_nWindowHeight + c_nResultListHeight; SetWindowPlacement(hwnd, &wndpl); } } // If it exists, just reset the content else { TCHAR* szMsg; TCHAR* szCaption; szMsg = LoadStringResource(IDS_CLRALLMSG); szCaption = LoadStringResource(IDS_CLRALLCAPTION); if(!szMsg || !szCaption) { DestroyWindow(hwnd); return; } if(MessageBox(hwnd, szMsg, szCaption, MB_ICONINFORMATION | MB_OKCANCEL) == IDOK) ListView_DeleteAllItems(GetDlgItem(hwnd, c_iResultListID)); else return; } // Enable the view options related to the result list HMENU hMenu; hMenu = GetMenu(hwnd); HMENU hmView = GetSubMenu(hMenu, c_iViewMenuPos); EnableMenuItem(hmView, c_iArrangeIconsPos, MF_BYPOSITION | MF_ENABLED); EnableMenuItem(hMenu, ID_VIEW_ARRANGEICONS_BYPATH, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, ID_VIEW_CHOOSECOLUMNS, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, ID_VIEW_REFRESH, MF_BYCOMMAND | MF_ENABLED); EnableWindow(GetDlgItem(hwnd, IDC_STOP), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_CLEARALL), TRUE); // Find out what to search for HWND hwComboBox = GetDlgItem(hwnd, IDC_DRIVELIST); // If zero is selected, search for everything. if(SendMessage(hwComboBox, CB_GETCURSEL, 0, 0)==0) SearchDB(0, hwnd); else { TCHAR szBuffer[MAX_PATH+1]; COMBOBOXEXITEM cbim; cbim.mask = CBEIF_TEXT; cbim.iItem = -1; cbim.pszText = szBuffer; cbim.cchTextMax = MAX_PATH; SendMessage(hwComboBox, CBEM_GETITEM, 0, reinterpret_cast(&cbim)); if(szBuffer[lstrlen(szBuffer)-1] != TEXT('\\')) lstrcat(szBuffer, TEXT("\\")); SearchDB(szBuffer, hwnd); } // Start animating the icon. HWND hwAnim = GetDlgItem(hwnd, c_iFindAnimID); Animate_Play(hwAnim, 0, -1, -1); } // Terminate the search. void StopSearch(HWND hwnd) { // Stop searching the database. StopSearchDB(); // Can't click "STOP" or anymore. EnableWindow(GetDlgItem(hwnd, IDC_STOP), FALSE); // Update with a search complete if not in menu. if(g_fInMenu == FALSE) UpdateStatus(hwnd, LoadStringResource(IDS_SEARCHDONE)); // Stop animating the icon. HWND hwAnim = GetDlgItem(hwnd, c_iFindAnimID); Animate_Stop(hwAnim); } // Remove result window. void ClearResults(HWND hwnd) { TCHAR* szMsg; TCHAR* szCaption; szMsg = LoadStringResource(IDS_CLRALLMSG); szCaption = LoadStringResource(IDS_CLRALLCAPTION); if(!szMsg || !szCaption) { DestroyWindow(hwnd); return; } if(MessageBox(hwnd, szMsg, szCaption, MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) return; // Stop the search StopSearch(hwnd); // Get rid of all result-related windows. HWND hwCtrl; if( (hwCtrl = GetDlgItem(hwnd, c_iResultListID)) != 0) DestroyWindow(hwCtrl); if( (hwCtrl = GetDlgItem(hwnd, c_iStatusBarID)) != 0) DestroyWindow(hwCtrl); if( (hwCtrl = GetDlgItem(hwnd, c_iResultFrameID)) != 0) DestroyWindow(hwCtrl); // Restore original window size. RECT rect; GetWindowRect(hwnd, &rect); MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left, g_nWindowHeight, TRUE); // Do cleanup if we were maximized if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_MAXIMIZE) { // Set correct restore position WINDOWPLACEMENT wndpl; GetWindowPlacement(hwnd, &wndpl); wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + g_nWindowHeight; SetWindowPlacement(hwnd, &wndpl); } // Disable the view options related to the result list HMENU hMenu; hMenu = GetMenu(hwnd); EnableMenuItem(hMenu, ID_FILE_DOWNLOADPATCH, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, ID_FILE_PROPERTIES, MF_BYCOMMAND | MF_GRAYED); HMENU hmView = GetSubMenu(hMenu, c_iViewMenuPos); EnableMenuItem(hmView, c_iArrangeIconsPos, MF_BYPOSITION | MF_GRAYED); EnableMenuItem(hMenu, ID_VIEW_ARRANGEICONS_BYPATH, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, ID_VIEW_CHOOSECOLUMNS, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, ID_VIEW_REFRESH, MF_BYCOMMAND | MF_GRAYED); EnableWindow(GetDlgItem(hwnd, IDC_CLEARALL), FALSE); } // Subclass for listview, customize painting. LRESULT CALLBACK LVWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) { // Only bother if it's a PAINT message and no items are in listbox. if((uiMsg == WM_PAINT) && (ListView_GetItemCount(hwnd) == 0)) { // Get rectangle for text. RECT rc; GetWindowRect(hwnd, &rc); POINT pt; pt.x = rc.left; pt.y = rc.top; ScreenToClient(hwnd, &pt); rc.left = pt.x; rc.top = pt.y; pt.x = rc.right; pt.y = rc.bottom; ScreenToClient(hwnd, &pt); rc.right = pt.x; rc.bottom = pt.y; HWND hwHeader = ListView_GetHeader(hwnd); if(hwHeader) { RECT rcHeader; Header_GetItemRect(hwHeader, 0, &rcHeader); rc.top += rcHeader.bottom; } rc.top += 10; // Do the default painting of the listview paint. InvalidateRect(hwnd, &rc, TRUE); CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam); HDC hdc = GetDC(hwnd); SaveDC(hdc); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(hdc, GetSysColor(COLOR_WINDOW)); HFONT hFont = static_cast(GetStockObject(ANSI_VAR_FONT)); SelectObject(hdc, hFont); // Get message to print. TCHAR* szMsg = LoadStringResource(IDS_EMPTYLIST); DrawText(hdc, szMsg, -1, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP); RestoreDC(hdc, -1); ReleaseDC(hwnd, hdc); delete szMsg; return 0; } else if(uiMsg == WM_NOTIFY) { HWND hwHeader = ListView_GetHeader(hwnd); if(hwHeader) { if(static_cast(wParam) == GetDlgCtrlID(hwHeader)) { LPNMHEADER pHdr = reinterpret_cast(lParam); if(pHdr->hdr.code == HDN_ITEMCHANGED) { RECT rc; GetClientRect(hwHeader, &rc); POINT pt; pt.y = rc.bottom; ClientToScreen(hwHeader, &pt); ScreenToClient(hwnd, &pt); GetClientRect(hwnd, &rc); rc.top = pt.y; InvalidateRect(hwnd, &rc, TRUE); } } } return CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam); } else return CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam); } // Allocate a new string and copy a string resource into it. TCHAR* LoadStringResource(UINT uID) { TCHAR szBuffer[c_nMaxStringLength]; TCHAR* szRet = 0; if(LoadString(g_hinst, uID, szBuffer, c_nMaxStringLength)) { szRet = new TCHAR[lstrlen(szBuffer)+1]; if(szRet) lstrcpy(szRet, szBuffer); } return szRet; } // Print an error message box in hwnd, with string resource with id as // message. void Error(HWND hwnd, UINT id) { PTSTR szMsg = LoadStringResource(id); PTSTR szCaption = LoadStringResource(IDS_ERROR); if(szMsg && szCaption) MessageBox(hwnd, szMsg, szCaption, MB_OK | MB_ICONERROR); if(szMsg) delete szMsg; if(szCaption) delete szCaption; }