Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

661 lines
22 KiB

  1. //
  2. // LISTVIEW.C
  3. //
  4. #include "sigverif.h"
  5. HWND g_hListView = NULL;
  6. HWND g_hStatus = NULL;
  7. BOOL g_bSortOrder[] = { FALSE, FALSE, FALSE, FALSE, FALSE};
  8. RECT g_Rect;
  9. //
  10. // Initialize the image lists for the icons in the listview control.
  11. //
  12. BOOL WINAPI ListView_SetImageLists(HWND hwndList)
  13. {
  14. SHFILEINFO sfi;
  15. HIMAGELIST himlSmall;
  16. HIMAGELIST himlLarge;
  17. BOOL bSuccess = TRUE;
  18. TCHAR szDriveRoot[MAX_PATH];
  19. MyGetWindowsDirectory(szDriveRoot, cA(szDriveRoot));
  20. szDriveRoot[3] = 0;
  21. himlSmall = (HIMAGELIST)SHGetFileInfo((LPCTSTR)szDriveRoot,
  22. 0,
  23. &sfi,
  24. sizeof(SHFILEINFO),
  25. SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
  26. himlLarge = (HIMAGELIST)SHGetFileInfo((LPCTSTR)szDriveRoot,
  27. 0,
  28. &sfi,
  29. sizeof(SHFILEINFO),
  30. SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
  31. if (himlSmall && himlLarge) {
  32. ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL);
  33. ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL);
  34. } else {
  35. bSuccess = FALSE;
  36. }
  37. return bSuccess;
  38. }
  39. //
  40. // Insert everything from the g_App.lpFileList into the listview control.
  41. //
  42. void ListView_InsertItems(void)
  43. {
  44. LPFILENODE lpFileNode;
  45. LV_ITEM lvi;
  46. TCHAR szBuffer[MAX_PATH];
  47. LPTSTR lpString;
  48. int iRet;
  49. BOOL bMirroredApp;
  50. HRESULT hr;
  51. bMirroredApp = (GetWindowLong(g_App.hDlg, GWL_EXSTYLE) & WS_EX_LAYOUTRTL);
  52. for (lpFileNode=g_App.lpFileList;lpFileNode;lpFileNode=lpFileNode->next) {
  53. if (lpFileNode->bScanned &&
  54. !lpFileNode->bSigned) {
  55. SetCurrentDirectory(lpFileNode->lpDirName);
  56. //
  57. // Initialize lvi and insert the filename and icon into the first column.
  58. //
  59. ZeroMemory(&lvi, sizeof(LV_ITEM));
  60. lvi.mask = LVIF_TEXT;
  61. lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  62. lvi.iImage = lpFileNode->iIcon;
  63. lvi.lParam = (LPARAM) lpFileNode;
  64. lvi.iSubItem = 0;
  65. lvi.pszText = lpFileNode->lpFileName;
  66. lvi.iItem = MAX_INT;
  67. lvi.iItem = ListView_InsertItem(g_hListView, &lvi);
  68. //
  69. // Insert the directory name into the second column.
  70. //
  71. lvi.mask = LVIF_TEXT;
  72. lvi.iSubItem = 1;
  73. lvi.pszText = lpFileNode->lpDirName;
  74. ListView_SetItem(g_hListView, &lvi);
  75. //
  76. // Get the date format, so we are localizable...
  77. //
  78. MyLoadString(szBuffer, cA(szBuffer), IDS_UNKNOWN);
  79. iRet = GetDateFormat(LOCALE_SYSTEM_DEFAULT,
  80. bMirroredApp ?
  81. DATE_RTLREADING | DATE_SHORTDATE :
  82. DATE_SHORTDATE,
  83. &lpFileNode->LastModified,
  84. NULL,
  85. NULL,
  86. 0);
  87. if (iRet) {
  88. lpString = MALLOC((iRet + 1) * sizeof(TCHAR));
  89. if (lpString) {
  90. iRet = GetDateFormat(LOCALE_SYSTEM_DEFAULT,
  91. bMirroredApp ?
  92. DATE_RTLREADING | DATE_SHORTDATE :
  93. DATE_SHORTDATE,
  94. &lpFileNode->LastModified,
  95. NULL,
  96. lpString,
  97. iRet);
  98. if (iRet) {
  99. hr = StringCchCopy(szBuffer, cA(szBuffer), lpString);
  100. if (FAILED(hr) && (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
  101. //
  102. // If we failed to copy the date into our buffer for
  103. // some reason other than insufficient buffer space,
  104. // then set the date to the empty string.
  105. //
  106. szBuffer[0] = TEXT('\0');
  107. }
  108. }
  109. FREE(lpString);
  110. }
  111. }
  112. lvi.mask = LVIF_TEXT;
  113. lvi.iSubItem = 2;
  114. lvi.pszText = szBuffer;
  115. ListView_SetItem(g_hListView, &lvi);
  116. //
  117. // Insert the filetype string into the fourth column.
  118. //
  119. if (lpFileNode->lpTypeName) {
  120. //
  121. // Since this string is just being displayed in the UI, it
  122. // is OK if it gets truncated.
  123. //
  124. hr = StringCchCopy(szBuffer, cA(szBuffer), lpFileNode->lpTypeName);
  125. if (FAILED(hr) &&
  126. (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
  127. //
  128. // We encountered some error other than insufficient
  129. // buffer, so just set the buffer to the empty string,
  130. // since it value is not determined with this type of
  131. // failure.
  132. //
  133. szBuffer[0] = TEXT('\0');
  134. }
  135. } else {
  136. MyLoadString(szBuffer, cA(szBuffer), IDS_UNKNOWN);
  137. }
  138. lvi.mask = LVIF_TEXT;
  139. lvi.iSubItem = 3;
  140. lvi.pszText = szBuffer;
  141. ListView_SetItem(g_hListView, &lvi);
  142. //
  143. // Insert the version string into the fifth column.
  144. //
  145. if (lpFileNode->lpVersion) {
  146. //
  147. // Since this string is just being displayed in the UI, it
  148. // is OK if it gets truncated.
  149. //
  150. hr = StringCchCopy(szBuffer, cA(szBuffer), lpFileNode->lpVersion);
  151. if (FAILED(hr) &&
  152. (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
  153. //
  154. // We encountered some error other than insufficient
  155. // buffer, so just set the buffer to the empty string,
  156. // since it value is not determined with this type of
  157. // failure.
  158. //
  159. szBuffer[0] = TEXT('\0');
  160. }
  161. } else {
  162. MyLoadString(szBuffer, cA(szBuffer), IDS_NOVERSION);
  163. }
  164. lvi.mask = LVIF_TEXT;
  165. lvi.iSubItem = 4;
  166. lvi.pszText = szBuffer;
  167. ListView_SetItem(g_hListView, &lvi);
  168. }
  169. }
  170. }
  171. //
  172. // Initialize the listview dialog. First, we are going to load the global icon resource.
  173. // Then we are going to create a status window and the actual listview control.
  174. // Then we need to add the four columns and work out their default widths.
  175. //
  176. BOOL ListView_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  177. {
  178. LV_COLUMN lvc;
  179. RECT rect, rectResultsText, rectStatusBar, rectCancelButton, rectClient;
  180. TCHAR szBuffer[MAX_PATH];
  181. TCHAR szBuffer2[MAX_PATH];
  182. INT iCol = 0, iWidth = 0;
  183. HRESULT hr;
  184. UNREFERENCED_PARAMETER(hwndFocus);
  185. UNREFERENCED_PARAMETER(lParam);
  186. //
  187. // Load the global icon resource
  188. //
  189. if (g_App.hIcon) {
  190. SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR) g_App.hIcon);
  191. }
  192. //
  193. // Create the status window at the bottom of the dialog
  194. //
  195. g_hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
  196. NULL,
  197. hwnd,
  198. (UINT) IDC_STATUSWINDOW);
  199. //
  200. // Load the status string and fill it in with the correct values.
  201. //
  202. MyLoadString(szBuffer, cA(szBuffer), IDS_NUMFILES);
  203. hr = StringCchPrintf(szBuffer2,
  204. cA(szBuffer2),
  205. szBuffer,
  206. g_App.dwFiles,
  207. g_App.dwSigned,
  208. g_App.dwUnsigned,
  209. g_App.dwFiles - g_App.dwSigned - g_App.dwUnsigned);
  210. if (FAILED(hr) &&
  211. (hr != STRSAFE_E_INSUFFICIENT_BUFFER)) {
  212. //
  213. // We encountered some error other than insufficient
  214. // buffer, so just set the buffer to the empty string,
  215. // since it value is not determined with this type of
  216. // failure.
  217. //
  218. szBuffer2[0] = TEXT('\0');
  219. }
  220. SendMessage(g_hStatus, WM_SETTEXT, (WPARAM) 0, (LPARAM) szBuffer2);
  221. GetWindowRect(hwnd, &g_Rect);
  222. GetClientRect(hwnd, &rectClient);
  223. //
  224. // Get the windows RECT values for the dialog, the static text, and the status window.
  225. // We will use these values to figure out where to put the listview and the columns.
  226. //
  227. GetWindowRect(hwnd, &rect);
  228. GetWindowRect(GetDlgItem(hwnd, IDC_RESULTSTEXT), &rectResultsText);
  229. GetWindowRect(g_hStatus, &rectStatusBar);
  230. GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancelButton);
  231. MoveWindow(GetDlgItem(hwnd, IDCANCEL),
  232. (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
  233. ? (rect.right - rect.left) - (rectCancelButton.right - rectCancelButton.left) - (rect.right - rectResultsText.right)
  234. : (rect.right - rectResultsText.left) - (rectCancelButton.right - rectCancelButton.left) - (( 2 * (rectResultsText.left - rect.left)) / 3),
  235. (rectClient.bottom - rectClient.top) - (rectStatusBar.bottom - rectStatusBar.top) - (rectCancelButton.bottom - rectCancelButton.top) - 10,
  236. rectCancelButton.right - rectCancelButton.left,
  237. rectCancelButton.bottom - rectCancelButton.top,
  238. TRUE);
  239. GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancelButton);
  240. //
  241. // Create the listview window! I am using some really screwey logic to figure out how
  242. // big to make the listview and where to put it, but it seems to work.
  243. //
  244. g_hListView = CreateWindowEx(WS_EX_CLIENTEDGE,
  245. WC_LISTVIEW, TEXT(""),
  246. WS_TABSTOP | WS_VSCROLL | WS_VISIBLE | WS_CHILD | WS_BORDER |
  247. LVS_SINGLESEL | LVS_REPORT | LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS,
  248. ((rectResultsText.left - rect.left) * 2) / 3,
  249. (rectResultsText.bottom - rectResultsText.top) * 2,
  250. (rect.right - rect.left) - 2 * (rectResultsText.left - rect.left),
  251. rectCancelButton.top - rectResultsText.bottom - 20,
  252. hwnd,
  253. (HMENU) IDC_LISTVIEW,
  254. g_App.hInstance,
  255. NULL);
  256. //
  257. // If the CreateWindowEx failed, then bail.
  258. //
  259. if (!g_hListView) {
  260. return FALSE;
  261. }
  262. //
  263. // Initialize the icon lists
  264. //
  265. ListView_SetImageLists(g_hListView);
  266. //
  267. // Create the first listview column for the icon and the file name.
  268. //
  269. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
  270. lvc.fmt = LVCFMT_LEFT;
  271. lvc.cx = (rectResultsText.right - rectResultsText.left) / 5;
  272. lvc.pszText = szBuffer;
  273. MyLoadString(szBuffer, cA(szBuffer), IDS_COL_NAME);
  274. lvc.cchTextMax = MAX_PATH;
  275. ListView_InsertColumn(g_hListView, iCol++, &lvc);
  276. //
  277. // Create the second listview column for the directory name.
  278. //
  279. iWidth += lvc.cx;
  280. lvc.cx = (rectResultsText.right - rectResultsText.left) / 4;
  281. MyLoadString(szBuffer, cA(szBuffer), IDS_COL_FOLDER);
  282. ListView_InsertColumn(g_hListView, iCol++, &lvc);
  283. //
  284. // Create the third listview column for the date name.
  285. //
  286. iWidth += lvc.cx;
  287. lvc.cx = (rectResultsText.right - rectResultsText.left) / 6;
  288. lvc.fmt = LVCFMT_CENTER;
  289. MyLoadString(szBuffer, cA(szBuffer), IDS_COL_DATE);
  290. ListView_InsertColumn(g_hListView, iCol++, &lvc);
  291. //
  292. // Create the fourth listview column for the filetype string.
  293. //
  294. iWidth += lvc.cx;
  295. lvc.cx = (rectResultsText.right - rectResultsText.left) / 6;
  296. lvc.fmt = LVCFMT_CENTER;
  297. MyLoadString(szBuffer, cA(szBuffer), IDS_COL_TYPE);
  298. ListView_InsertColumn(g_hListView, iCol++, &lvc);
  299. //
  300. // Create the fifth listview column for the version string.
  301. //
  302. iWidth += lvc.cx;
  303. lvc.cx = (rectResultsText.right - rectResultsText.left) - iWidth - 5;
  304. lvc.fmt = LVCFMT_CENTER;
  305. MyLoadString(szBuffer, cA(szBuffer), IDS_COL_VERSION);
  306. ListView_InsertColumn(g_hListView, iCol++, &lvc);
  307. //
  308. // Now that the columns are set up, insert all the files in g_App.lpFileList!
  309. //
  310. ListView_InsertItems();
  311. //
  312. // Initialize the sorting order array to all FALSE.
  313. //
  314. g_bSortOrder[0] = FALSE;
  315. g_bSortOrder[1] = FALSE;
  316. g_bSortOrder[2] = FALSE;
  317. g_bSortOrder[3] = FALSE;
  318. SetForegroundWindow(g_App.hDlg);
  319. SetForegroundWindow(hwnd);
  320. SetFocus(GetDlgItem(hwnd, IDCANCEL));
  321. return TRUE;
  322. }
  323. //
  324. // This function checks to see how big the sizing rectangle will be. If the user is trying
  325. // to size the dialog to less than the values in g_Rect, then we will fix the rectangle values
  326. //
  327. BOOL ListView_OnSizing(HWND hwnd, WPARAM wParam, LPARAM lParam)
  328. {
  329. RECT rect;
  330. LPRECT lpRect = (LPRECT) lParam;
  331. BOOL bRet = FALSE;
  332. UNREFERENCED_PARAMETER(wParam);
  333. GetWindowRect(hwnd, &rect);
  334. if ((lpRect->right - lpRect->left) < (g_Rect.right - g_Rect.left)) {
  335. lpRect->left = rect.left;
  336. lpRect->right = lpRect->left + (g_Rect.right - g_Rect.left);
  337. bRet = TRUE;
  338. }
  339. if ((lpRect->bottom - lpRect->top) < (g_Rect.bottom - g_Rect.top)) {
  340. lpRect->top = rect.top;
  341. lpRect->bottom = lpRect->top + (g_Rect.bottom - g_Rect.top);
  342. bRet = TRUE;
  343. }
  344. return bRet;
  345. }
  346. //
  347. // This function allows us to resize the listview control and status windows when the
  348. // user resizes the results dialog. Thankfully, we can make everything relative using
  349. // the RECT values for the main dialog, the static text, and the status window.
  350. //
  351. void ListView_ResizeWindow(HWND hwnd)
  352. {
  353. RECT rect, rectResultsText, rectStatusBar, rectCancelButton, rectClient;
  354. BOOL bMirroredApp;
  355. bMirroredApp = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL);
  356. GetWindowRect(hwnd, &rect);
  357. if ((rect.right - rect.left) < (g_Rect.right - g_Rect.left)) {
  358. MoveWindow(hwnd,
  359. rect.left,
  360. rect.top,
  361. g_Rect.right - g_Rect.left,
  362. rect.bottom - rect.top,
  363. TRUE);
  364. }
  365. if ((rect.bottom - rect.top) < (g_Rect.bottom - g_Rect.top)) {
  366. MoveWindow(hwnd,
  367. rect.left,
  368. rect.top,
  369. rect.right - rect.left,
  370. g_Rect.bottom - g_Rect.top,
  371. TRUE);
  372. }
  373. GetClientRect(hwnd, &rectClient);
  374. GetWindowRect(GetDlgItem(hwnd, IDC_RESULTSTEXT), &rectResultsText);
  375. GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancelButton);
  376. GetWindowRect(g_hStatus, &rectStatusBar);
  377. MoveWindow(g_hStatus,
  378. 0,
  379. (rect.bottom - rect.top) - (rectStatusBar.bottom - rectStatusBar.top),
  380. rect.right - rect.left,
  381. rectStatusBar.bottom - rectStatusBar.top,
  382. TRUE);
  383. MoveWindow(GetDlgItem(hwnd, IDCANCEL),
  384. (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
  385. ? (rect.right - rect.left) - (rectCancelButton.right - rectCancelButton.left) - (rect.right - rectResultsText.right)
  386. : (rect.right - rectResultsText.left) - (rectCancelButton.right - rectCancelButton.left) - (( 2 * (rectResultsText.left - rect.left)) / 3),
  387. (rectClient.bottom - rectClient.top) - (rectStatusBar.bottom - rectStatusBar.top) - (rectCancelButton.bottom - rectCancelButton.top) - 10,
  388. rectCancelButton.right - rectCancelButton.left,
  389. rectCancelButton.bottom - rectCancelButton.top,
  390. TRUE);
  391. GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancelButton);
  392. MoveWindow(g_hListView,
  393. bMirroredApp
  394. ? ((rect.right - rectResultsText.right) * 2) / 3
  395. : ((rectResultsText.left - rect.left) * 2) / 3,
  396. (rectResultsText.bottom - rectResultsText.top) * 2,
  397. bMirroredApp
  398. ? (rect.right - rect.left) - 2 * (rect.right - rectResultsText.right)
  399. : (rect.right - rect.left) - 2 * (rectResultsText.left - rect.left),
  400. rectCancelButton.top - rectResultsText.bottom - 20,
  401. TRUE);
  402. }
  403. //
  404. // This function is a callback that returns a value for ListView_SortItems.
  405. // ListView_SortItems wants a negative, zero, or positive number.
  406. // Since CompareString returns 1,2,3 we just subtract 2 from the return value.
  407. //
  408. // We use the g_bSortOrder array to figure out which way we have sorted in the past.
  409. //
  410. // Warning: we don't check for error values from CompareString
  411. //
  412. int CALLBACK ListView_CompareNames(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  413. {
  414. LPFILENODE lpFileNode1;
  415. LPFILENODE lpFileNode2;
  416. FILETIME FileTime1, FileTime2;
  417. int iResult = 2;
  418. //
  419. // Depending on the sort order, we swap the order of comparison
  420. //
  421. if (g_bSortOrder[lParamSort]) {
  422. lpFileNode2 = (LPFILENODE) lParam1;
  423. lpFileNode1 = (LPFILENODE) lParam2;
  424. } else {
  425. lpFileNode1 = (LPFILENODE) lParam1;
  426. lpFileNode2 = (LPFILENODE) lParam2;
  427. }
  428. switch (lParamSort) {
  429. case 0:
  430. //
  431. // We are comparing the file names
  432. //
  433. iResult = CompareString(LOCALE_SYSTEM_DEFAULT,
  434. NORM_IGNORECASE | NORM_IGNOREWIDTH,
  435. lpFileNode1->lpFileName,
  436. -1,
  437. lpFileNode2->lpFileName,
  438. -1);
  439. break;
  440. case 1:
  441. //
  442. // We are comparing the directory names
  443. //
  444. iResult = CompareString(LOCALE_SYSTEM_DEFAULT,
  445. NORM_IGNORECASE | NORM_IGNOREWIDTH,
  446. lpFileNode1->lpDirName,
  447. -1,
  448. lpFileNode2->lpDirName,
  449. -1);
  450. break;
  451. case 2:
  452. //
  453. // We are comparing the LastWriteTime's between the two files.
  454. //
  455. SystemTimeToFileTime(&lpFileNode1->LastModified, &FileTime1);
  456. SystemTimeToFileTime(&lpFileNode2->LastModified, &FileTime2);
  457. iResult = CompareFileTime(&FileTime1, &FileTime2);
  458. return iResult;
  459. break;
  460. case 3:
  461. //
  462. // We are comparing the filetype strings
  463. //
  464. iResult = CompareString(LOCALE_SYSTEM_DEFAULT,
  465. NORM_IGNORECASE | NORM_IGNOREWIDTH,
  466. lpFileNode1->lpTypeName,
  467. -1,
  468. lpFileNode2->lpTypeName,
  469. -1);
  470. break;
  471. case 4:
  472. //
  473. // We are comparing the version strings
  474. //
  475. iResult = CompareString(LOCALE_SYSTEM_DEFAULT,
  476. NORM_IGNORECASE | NORM_IGNOREWIDTH,
  477. lpFileNode1->lpVersion,
  478. -1,
  479. lpFileNode2->lpVersion,
  480. -1);
  481. break;
  482. }
  483. return(iResult - 2);
  484. }
  485. //
  486. // This function handles the clicks on the column headers and calls ListView_SortItems with the
  487. // ListView_CompareNames callback previously defined. It then toggles the sortorder for that column.
  488. //
  489. LRESULT ListView_NotifyHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  490. {
  491. NMHDR *lpnmhdr = (NMHDR *) lParam;
  492. NM_LISTVIEW *lpnmlv = (NM_LISTVIEW *) lParam;
  493. UNREFERENCED_PARAMETER(hwnd);
  494. UNREFERENCED_PARAMETER(uMsg);
  495. UNREFERENCED_PARAMETER(wParam);
  496. switch (lpnmhdr->code) {
  497. case LVN_COLUMNCLICK:
  498. switch (lpnmlv->iSubItem) {
  499. case 0:
  500. case 1:
  501. case 2:
  502. case 3:
  503. case 4: ListView_SortItems(lpnmlv->hdr.hwndFrom, ListView_CompareNames, (LPARAM) lpnmlv->iSubItem);
  504. g_bSortOrder[lpnmlv->iSubItem] = !(g_bSortOrder[lpnmlv->iSubItem]);
  505. break;
  506. }
  507. break;
  508. }
  509. return 0;
  510. }
  511. //
  512. // The only thing we look for here is the IDCANCEL if the user hit ESCAPE
  513. //
  514. void ListView_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  515. {
  516. UNREFERENCED_PARAMETER(hwndCtl);
  517. UNREFERENCED_PARAMETER(codeNotify);
  518. switch (id) {
  519. case IDCANCEL:
  520. SendMessage(hwnd, WM_CLOSE, 0, 0);
  521. break;
  522. }
  523. }
  524. INT_PTR CALLBACK ListView_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  525. {
  526. BOOL fProcessed = TRUE;
  527. switch (uMsg) {
  528. HANDLE_MSG(hwnd, WM_INITDIALOG, ListView_OnInitDialog);
  529. HANDLE_MSG(hwnd, WM_COMMAND, ListView_OnCommand);
  530. case WM_NOTIFY:
  531. return ListView_NotifyHandler(hwnd, uMsg, wParam, lParam);
  532. case WM_CLOSE:
  533. if (g_hStatus) {
  534. DestroyWindow(g_hStatus);
  535. g_hStatus = NULL;
  536. }
  537. if (g_hListView) {
  538. DestroyWindow(g_hListView);
  539. g_hListView = NULL;
  540. }
  541. EndDialog(hwnd, ID_CLOSE);
  542. break;
  543. case WM_SIZING:
  544. fProcessed = ListView_OnSizing(hwnd, wParam, lParam);
  545. break;
  546. case WM_SIZE:
  547. ListView_ResizeWindow(hwnd);
  548. break;
  549. default: fProcessed = FALSE;
  550. }
  551. return fProcessed;
  552. }