[DEVMGR]
[reactos.git] / reactos / dll / win32 / devmgr / devmgmt / MainWindow.cpp
1 /*
2 * PROJECT: ReactOS Device Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/devmgr/devmgmt/MainWindow.cpp
5 * PURPOSE: Implements the main container window for the device view
6 * COPYRIGHT: Copyright 2014 - 2015 Ged Murphy <gedmurphy@reactos.org>
7 */
8
9
10 #include "precomp.h"
11 #include "devmgmt.h"
12 #include "MainWindow.h"
13
14
15 /* DATA *****************************************************/
16
17 #define BTN_PROPERTIES 0
18 #define BTN_SCAN_HARDWARE 1
19 #define BTN_ENABLE_DRV 2
20 #define BTN_DISABLE_DRV 3
21 #define BTN_UPDATE_DRV 4
22 #define BTN_UNINSTALL_DRV 5
23
24 #define REFRESH_TIMER 1
25
26 HINSTANCE g_hThisInstance = NULL;
27 HINSTANCE g_hParentInstance = NULL;
28
29 // menu hints
30 static const MENU_HINT MainMenuHintTable[] =
31 {
32 // File Menu
33 { IDC_EXIT, IDS_HINT_EXIT },
34
35 // Action Menu
36 { IDC_PROPERTIES, IDS_HINT_PROPERTIES },
37 { IDC_SCAN_HARDWARE, IDS_HINT_SCAN },
38 { IDC_ENABLE_DRV, IDS_HINT_ENABLE },
39 { IDC_DISABLE_DRV, IDS_HINT_DISABLE },
40 { IDC_UPDATE_DRV, IDS_HINT_UPDATE },
41 { IDC_UNINSTALL_DRV, IDS_HINT_UNINSTALL },
42 { IDC_ADD_HARDWARE, IDS_HINT_ADD },
43
44
45 // View Menu
46 { IDC_DEVBYTYPE, IDS_HINT_DEV_BY_TYPE},
47 { IDC_DEVBYCONN, IDS_HINT_DEV_BY_CONN},
48 { IDC_RESBYTYPE, IDS_HINT_RES_BY_TYPE},
49 { IDC_RESBYCONN, IDS_HINT_RES_BY_TYPE},
50 { IDC_SHOWHIDDEN, IDS_HINT_SHOW_HIDDEN },
51
52 { IDC_ABOUT, IDS_HINT_ABOUT }
53
54 };
55
56
57 // system menu hints
58 static const MENU_HINT SystemMenuHintTable[] =
59 {
60 {SC_RESTORE, IDS_HINT_SYS_RESTORE},
61 {SC_MOVE, IDS_HINT_SYS_MOVE},
62 {SC_SIZE, IDS_HINT_SYS_SIZE},
63 {SC_MINIMIZE, IDS_HINT_SYS_MINIMIZE},
64 {SC_MAXIMIZE, IDS_HINT_SYS_MAXIMIZE},
65 {SC_CLOSE, IDS_HINT_SYS_CLOSE}
66 };
67
68 static TBBUTTON TbButtons[] =
69 {
70 { BTN_PROPERTIES, IDC_PROPERTIES, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
71 { BTN_SCAN_HARDWARE, IDC_SCAN_HARDWARE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
72 { 2, IDC_STATIC, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0 },
73 { BTN_ENABLE_DRV, IDC_ENABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
74 { BTN_DISABLE_DRV, IDC_DISABLE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
75 { BTN_UPDATE_DRV, IDC_UPDATE_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },
76 { BTN_UNINSTALL_DRV, IDC_UNINSTALL_DRV, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 }
77 };
78
79
80 /* PUBLIC METHODS **********************************************/
81
82 CDeviceManager::CDeviceManager(void) :
83 m_hMainWnd(NULL),
84 m_hStatusBar(NULL),
85 m_hToolBar(NULL),
86 m_CmdShow(0),
87 m_RefreshPending(false)
88 {
89 m_szMainWndClass = L"DevMgmtWndClass";
90 }
91
92 CDeviceManager::~CDeviceManager(void)
93 {
94 }
95
96 bool
97 CDeviceManager::Create(_In_ HWND /*hWndParent*/,
98 _In_ HINSTANCE hInst,
99 _In_opt_z_ LPCWSTR /*lpMachineName*/,
100 _In_ int nCmdShow)
101 {
102 CDeviceManager MainWindow;
103 INITCOMMONCONTROLSEX icex;
104 CAtlStringW szAppName;
105 int Ret = 1;
106
107 // Store the instances
108 g_hParentInstance = hInst;
109 g_hThisInstance = GetModuleHandleW(L"devmgr.dll");
110
111 // Initialize common controls
112 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
113 icex.dwICC = ICC_BAR_CLASSES | ICC_COOL_CLASSES;
114 InitCommonControlsEx(&icex);
115
116 // Load the application name
117 if (szAppName.LoadStringW(g_hThisInstance, IDS_APPNAME))
118 {
119 // Initialize the main window
120 if (MainWindow.Initialize(szAppName, nCmdShow))
121 {
122 // Run the application
123 Ret = MainWindow.Run();
124
125 // Uninitialize the main window
126 MainWindow.Uninitialize();
127 }
128 }
129
130 return (Ret == 0);
131 }
132
133
134 /* PRIVATE METHODS **********************************************/
135
136 bool
137 CDeviceManager::Initialize(_In_z_ LPCTSTR lpCaption,
138 _In_ int nCmdShow)
139 {
140 CAtlStringW szCaption;
141 WNDCLASSEXW wc = {0};
142
143 // Store the show window value
144 m_CmdShow = nCmdShow;
145
146 // Setup the window class struct
147 wc.cbSize = sizeof(WNDCLASSEXW);
148 wc.lpfnWndProc = MainWndProc;
149 wc.hInstance = g_hThisInstance;
150 wc.hIcon = LoadIcon(g_hThisInstance, MAKEINTRESOURCEW(IDI_MAIN_ICON));
151 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
152 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
153 wc.lpszMenuName = MAKEINTRESOURCEW(IDR_MAINMENU);
154 wc.lpszClassName = m_szMainWndClass;
155 wc.hIconSm = (HICON)LoadImage(g_hThisInstance,
156 MAKEINTRESOURCE(IDI_MAIN_ICON),
157 IMAGE_ICON,
158 16,
159 16,
160 LR_SHARED);
161
162 // Register the window
163 if (RegisterClassExW(&wc))
164 {
165 // Create the main window and store the object pointer
166 m_hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE,
167 m_szMainWndClass,
168 lpCaption,
169 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
170 CW_USEDEFAULT,
171 CW_USEDEFAULT,
172 550,
173 500,
174 NULL,
175 NULL,
176 g_hThisInstance,
177 this);
178 }
179
180 // Return creation result
181 return !!(m_hMainWnd);
182 }
183
184 void
185 CDeviceManager::Uninitialize(void)
186 {
187 // Unregister the window class
188 UnregisterClassW(m_szMainWndClass, g_hThisInstance);
189 }
190
191 int
192 CDeviceManager::Run(void)
193 {
194 MSG Msg;
195
196 // Pump the message queue
197 while (GetMessageW(&Msg, NULL, 0, 0 ) != 0)
198 {
199 TranslateMessage(&Msg);
200 DispatchMessageW(&Msg);
201 }
202
203 return 0;
204 }
205
206 bool
207 CDeviceManager::MainWndMenuHint(_In_ WORD CmdId,
208 _In_ const MENU_HINT *HintArray,
209 _In_ DWORD HintsCount,
210 _In_ UINT DefHintId)
211 {
212 bool Found = false;
213 const MENU_HINT *LastHint;
214 UINT HintId = DefHintId;
215
216 LastHint = HintArray + HintsCount;
217 while (HintArray != LastHint)
218 {
219 if (HintArray->CmdId == CmdId)
220 {
221 HintId = HintArray->HintId;
222 Found = true;
223 break;
224 }
225 HintArray++;
226 }
227
228 StatusBarLoadString(m_hStatusBar,
229 SB_SIMPLEID,
230 g_hThisInstance,
231 HintId);
232
233 return Found;
234 }
235
236 void
237 CDeviceManager::UpdateStatusBar(_In_ bool InMenuLoop)
238 {
239 SendMessageW(m_hStatusBar,
240 SB_SIMPLE,
241 (WPARAM)InMenuLoop,
242 0);
243 }
244
245 bool
246 CDeviceManager::RefreshView(_In_ ViewType Type,
247 _In_ bool ScanForChanges)
248 {
249 UINT CheckId = 0;
250
251 // Refreshed the cached view
252 m_DeviceView->Refresh(Type, ScanForChanges, true);
253
254 // Get the menu item id
255 switch (Type)
256 {
257 case DevicesByType:
258 CheckId = IDC_DEVBYTYPE;
259 break;
260
261 case DevicesByConnection:
262 CheckId = IDC_DEVBYCONN;
263 break;
264
265 case ResourcesByType:
266 CheckId = IDC_RESBYTYPE;
267 break;
268
269 case ResourcesByConnection:
270 CheckId = IDC_RESBYCONN;
271 break;
272
273 default:
274 ATLASSERT(FALSE);
275 break;
276 }
277
278 // Set the new check item
279 CheckMenuRadioItem(m_hMenu,
280 IDC_DEVBYTYPE,
281 IDC_RESBYCONN,
282 CheckId,
283 MF_BYCOMMAND);
284
285 return true;
286 }
287
288 bool
289 CDeviceManager::CreateToolBar(void)
290 {
291 TBADDBITMAP TbAddBitmap;
292
293 DWORD dwStyles = WS_CHILDWINDOW | TBSTYLE_FLAT | TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER;
294 DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
295
296 // Create the toolbar window
297 m_hToolBar = CreateWindowExW(dwExStyles,
298 TOOLBARCLASSNAME,
299 NULL,
300 dwStyles,
301 0, 0, 0, 0,
302 m_hMainWnd,
303 (HMENU)IDC_TOOLBAR,
304 g_hThisInstance,
305 NULL);
306 if (m_hToolBar == NULL)
307 return FALSE;
308
309 // Don't show clipped buttons
310 SendMessageW(m_hToolBar,
311 TB_SETEXTENDEDSTYLE,
312 0,
313 TBSTYLE_EX_HIDECLIPPEDBUTTONS);
314
315 SendMessageW(m_hToolBar, TB_SETBITMAPSIZE, 0, MAKELONG(16, 16));
316
317 // Set the struct size, the toobar needs this...
318 SendMessageW(m_hToolBar,
319 TB_BUTTONSTRUCTSIZE,
320 sizeof(TBBUTTON),
321 0);
322
323 TbAddBitmap.hInst = g_hThisInstance;
324 TbAddBitmap.nID = IDB_TOOLBAR;
325 SendMessageW(m_hToolBar, TB_ADDBITMAP, _countof(TbButtons), (LPARAM)&TbAddBitmap);
326
327 SendMessageW(m_hToolBar, TB_ADDBUTTONSW, _countof(TbButtons), (LPARAM)TbButtons);
328 SendMessageW(m_hToolBar, TB_AUTOSIZE, 0, 0);
329
330 if (TRUE)
331 {
332 ShowWindow(m_hToolBar, SW_SHOW);
333 }
334
335 return TRUE;
336 }
337
338 bool
339 CDeviceManager::CreateStatusBar(void)
340 {
341 int StatWidths[] = {110, -1}; // widths of status bar
342 bool bRet = FALSE;
343
344 // Create the status bar
345 m_hStatusBar = CreateWindowExW(0,
346 STATUSCLASSNAME,
347 NULL,
348 WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
349 0, 0, 0, 0,
350 m_hMainWnd,
351 (HMENU)IDC_STATUSBAR,
352 g_hThisInstance,
353 NULL);
354 if (m_hStatusBar)
355 {
356 // Set the width
357 bRet = (SendMessageW(m_hStatusBar,
358 SB_SETPARTS,
359 sizeof(StatWidths) / sizeof(int),
360 (LPARAM)StatWidths) != 0);
361 }
362
363 return bRet;
364 }
365
366 void CDeviceManager::UpdateToolbar()
367 {
368 WORD State;
369
370 CNode *Node = m_DeviceView->GetSelectedNode();
371
372 // properties button
373 if (Node->HasProperties())
374 {
375 State = TBSTATE_ENABLED;
376 }
377 else
378 {
379 State = TBSTATE_HIDDEN;
380 }
381 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_PROPERTIES, MAKELPARAM(State, 0));
382 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UPDATE_DRV, MAKELPARAM(State, 0)); //hack
383 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_UNINSTALL_DRV, MAKELPARAM(State, 0)); // hack
384
385 // enable driver button
386 if (Node->GetNodeType() == DeviceNode &&
387 dynamic_cast<CDeviceNode *>(Node)->IsDisabled())
388 {
389 State = TBSTATE_ENABLED;
390 }
391 else
392 {
393 State = TBSTATE_HIDDEN;
394 }
395 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_ENABLE_DRV, MAKELPARAM(State, 0));
396
397 // disable driver button
398 if (Node->GetNodeType() == DeviceNode &&
399 dynamic_cast<CDeviceNode *>(Node)->CanDisable() &&
400 !dynamic_cast<CDeviceNode *>(Node)->IsDisabled())
401 {
402 State = TBSTATE_ENABLED;
403 }
404 else
405 {
406 State = TBSTATE_HIDDEN;
407 }
408 SendMessageW(m_hToolBar, TB_SETSTATE, IDC_DISABLE_DRV, MAKELPARAM(State, 0));
409 }
410
411 bool
412 CDeviceManager::StatusBarLoadString(_In_ HWND hStatusBar,
413 _In_ INT PartId,
414 _In_ HINSTANCE hInstance,
415 _In_ UINT uID)
416 {
417 CAtlStringW szMessage;
418 bool bRet = false;
419
420 // Load the string
421 if (szMessage.LoadStringW(hInstance, uID))
422 {
423 // Show the string on the status bar
424 bRet = (SendMessageW(hStatusBar,
425 SB_SETTEXT,
426 (WPARAM)PartId,
427 (LPARAM)szMessage.GetBuffer()) != 0);
428 }
429
430 return bRet;
431 }
432
433 LRESULT
434 CDeviceManager::OnCreate(_In_ HWND hwnd)
435 {
436 LRESULT RetCode;
437
438 RetCode = -1;
439 m_hMainWnd = hwnd;
440
441 // Store a handle to the main menu
442 m_hMenu = GetMenu(m_hMainWnd);
443
444 // Create the toolbar and statusbar
445 if (CreateToolBar() && CreateStatusBar())
446 {
447 // Create the device view object
448 m_DeviceView = new CDeviceView(m_hMainWnd);
449 if (m_DeviceView->Initialize())
450 {
451 // Do the initial scan
452 RefreshView(m_DeviceView->GetCurrentView(), true);
453
454 // Display the window according to the user request
455 ShowWindow(hwnd, m_CmdShow);
456 RetCode = 0;
457 }
458 }
459
460 return RetCode;
461 }
462
463 LRESULT
464 CDeviceManager::OnSize(void)
465 {
466 RECT rcClient, rcTool, rcStatus;
467 INT lvHeight, iToolHeight, iStatusHeight;
468
469 // Autosize the toolbar
470 SendMessage(m_hToolBar, TB_AUTOSIZE, 0, 0);
471
472 // Get the toolbar rect and save the height
473 GetWindowRect(m_hToolBar, &rcTool);
474 iToolHeight = rcTool.bottom - rcTool.top;
475
476 // Resize the status bar
477 SendMessage(m_hStatusBar, WM_SIZE, 0, 0);
478
479 // Get the statusbar rect and save the height
480 GetWindowRect(m_hStatusBar, &rcStatus);
481 iStatusHeight = rcStatus.bottom - rcStatus.top;
482
483 // Get the full client rect
484 GetClientRect(m_hMainWnd, &rcClient);
485
486 // Calculate the remaining height for the treeview
487 lvHeight = rcClient.bottom - iToolHeight - iStatusHeight;
488
489 // Resize the device view
490 m_DeviceView->OnSize(0,
491 iToolHeight,
492 rcClient.right,
493 lvHeight);
494
495 return 0;
496 }
497
498 LRESULT
499 CDeviceManager::OnNotify(_In_ LPARAM lParam)
500 {
501 LPNMHDR NmHdr = (LPNMHDR)lParam;
502 LRESULT Ret = 0;
503
504 switch (NmHdr->code)
505 {
506 case TVN_SELCHANGED:
507 {
508 UpdateToolbar();
509 break;
510 }
511
512 case NM_DBLCLK:
513 {
514 m_DeviceView->DisplayPropertySheet();
515 break;
516 }
517
518 case NM_RCLICK:
519 {
520 Ret = m_DeviceView->OnRightClick(NmHdr);
521 break;
522 }
523
524 case NM_RETURN:
525 {
526 m_DeviceView->DisplayPropertySheet();
527 break;
528 }
529
530 case TTN_GETDISPINFO:
531 {
532 LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lParam;
533 lpttt->hinst = g_hThisInstance;
534
535 UINT_PTR idButton = lpttt->hdr.idFrom;
536 switch (idButton)
537 {
538 case IDC_PROPERTIES:
539 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_PROPERTIES);
540 break;
541 case IDC_SCAN_HARDWARE:
542 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SCAN);
543 break;
544 case IDC_ENABLE_DRV:
545 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_ENABLE);
546 break;
547 case IDC_DISABLE_DRV:
548 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_DISABLE);
549 break;
550 case IDC_UPDATE_DRV:
551 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE);
552 break;
553 case IDC_UNINSTALL_DRV:
554 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL);
555 break;
556 }
557 break;
558 }
559 }
560
561 return Ret;
562 }
563
564 LRESULT
565 CDeviceManager::OnContext(_In_ LPARAM lParam)
566 {
567 return m_DeviceView->OnContextMenu(lParam);
568 }
569
570 LRESULT
571 CDeviceManager::OnCommand(_In_ WPARAM wParam,
572 _In_ LPARAM /*lParam*/)
573 {
574 LRESULT RetCode = 0;
575 WORD Msg;
576
577 // Get the message
578 Msg = LOWORD(wParam);
579
580 switch (Msg)
581 {
582 case IDC_PROPERTIES:
583 case IDC_SCAN_HARDWARE:
584 case IDC_ENABLE_DRV:
585 case IDC_DISABLE_DRV:
586 case IDC_UPDATE_DRV:
587 case IDC_UNINSTALL_DRV:
588 case IDC_ADD_HARDWARE:
589 {
590 m_DeviceView->OnAction(Msg);
591 break;
592 }
593
594 case IDC_ACTIONMENU:
595 {
596 // Create a popup menu with all the actions for the selected node
597 HMENU hMenu = CreatePopupMenu();
598 m_DeviceView->CreateActionMenu(hMenu, true);
599
600 // Calculate where to put the menu
601 RECT rc;
602 GetMenuItemRect(m_hMainWnd, m_hMenu, 1, &rc);
603 LONG Height = rc.bottom - rc.top;
604
605 // Display the menu
606 TrackPopupMenuEx(hMenu,
607 TPM_RIGHTBUTTON,
608 rc.left,
609 rc.top + Height,
610 m_hMainWnd,
611 NULL);
612
613 DestroyMenu(hMenu);
614 break;
615 }
616
617 case IDC_DEVBYTYPE:
618 {
619 RefreshView(DevicesByType, false);
620 break;
621 }
622
623 case IDC_DEVBYCONN:
624 {
625 RefreshView(DevicesByConnection, false);
626 break;
627 }
628
629 case IDC_SHOWHIDDEN:
630 {
631 // Get the current state
632 UINT CurCheckState = GetMenuState(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND);
633 if (CurCheckState == MF_CHECKED)
634 {
635 m_DeviceView->SetHiddenDevices(false);
636 CheckMenuItem(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND | MF_UNCHECKED);
637 }
638 else if (CurCheckState == MF_UNCHECKED)
639 {
640 m_DeviceView->SetHiddenDevices(true);
641 CheckMenuItem(m_hMenu, IDC_SHOWHIDDEN, MF_BYCOMMAND | MF_CHECKED);
642 }
643 // Refresh the device view
644 RefreshView(m_DeviceView->GetCurrentView(), false);
645 break;
646 }
647
648 case IDC_ABOUT:
649 {
650 // Apportion blame
651 MessageBoxW(m_hMainWnd,
652 L"ReactOS Device Manager\r\nCopyright Ged Murphy 2015",
653 L"About",
654 MB_OK | MB_APPLMODAL);
655
656 // Set focus back to the treeview
657 m_DeviceView->SetFocus();
658 break;
659 }
660
661 case IDC_EXIT:
662 {
663 // Post a close message to the window
664 PostMessageW(m_hMainWnd,
665 WM_CLOSE,
666 0,
667 0);
668 break;
669 }
670
671 default:
672 // We didn't handle it
673 RetCode = -1;
674 break;
675 }
676
677 return RetCode;
678 }
679
680 void
681 CDeviceManager::OnActivate(void)
682 {
683 m_DeviceView->SetFocus();
684 }
685
686 LRESULT
687 CDeviceManager::OnDestroy(void)
688 {
689 // Uninitialize the device view
690 m_DeviceView->Uninitialize();
691
692 // Kill the object
693 delete m_DeviceView;
694 m_DeviceView = NULL;
695
696 // Clear the user data pointer
697 SetWindowLongPtr(m_hMainWnd, GWLP_USERDATA, 0);
698
699 // Break the message loop
700 PostQuitMessage(0);
701
702 return 0;
703 }
704
705 LRESULT CALLBACK
706 CDeviceManager::MainWndProc(_In_ HWND hwnd,
707 _In_ UINT msg,
708 _In_ WPARAM wParam,
709 _In_ LPARAM lParam)
710 {
711 CDeviceManager *This;
712 LRESULT RetCode = 0;
713
714 // Get the object pointer from window context
715 This = (CDeviceManager *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
716 if (This == NULL)
717 {
718 // Check that this isn't a create message
719 if (msg != WM_CREATE)
720 {
721 // Don't handle null info pointer
722 goto HandleDefaultMessage;
723 }
724 }
725
726 switch(msg)
727 {
728 case WM_CREATE:
729 {
730 // Get the object pointer from the create param
731 This = (CDeviceManager *)((LPCREATESTRUCT)lParam)->lpCreateParams;
732
733 // Store the pointer in the window's global user data
734 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)This);
735
736 // Call the create handler
737 RetCode = This->OnCreate(hwnd);
738 break;
739 }
740
741 case WM_SIZE:
742 {
743 RetCode = This->OnSize();
744 break;
745 }
746
747 case WM_NOTIFY:
748 {
749 RetCode = This->OnNotify(lParam);
750 break;
751 }
752
753 case WM_CONTEXTMENU:
754 {
755 RetCode = This->OnContext(lParam);
756 break;
757 }
758
759 case WM_MENUSELECT:
760 {
761 if (This->m_hStatusBar != NULL)
762 {
763 if (!This->MainWndMenuHint(LOWORD(wParam),
764 MainMenuHintTable,
765 sizeof(MainMenuHintTable) / sizeof(MainMenuHintTable[0]),
766 IDS_HINT_BLANK))
767 {
768 This->MainWndMenuHint(LOWORD(wParam),
769 SystemMenuHintTable,
770 sizeof(SystemMenuHintTable) / sizeof(SystemMenuHintTable[0]),
771 IDS_HINT_BLANK);
772 }
773 }
774
775 break;
776 }
777
778 case WM_COMMAND:
779 {
780 // Handle the command message
781 RetCode = This->OnCommand(wParam, lParam);
782 if (RetCode == -1)
783 {
784 // Hand it off to the default message handler
785 goto HandleDefaultMessage;
786 }
787 break;
788 }
789
790 case WM_DEVICECHANGE:
791 {
792 if (wParam == DBT_DEVNODES_CHANGED)
793 {
794 //
795 // The OS can send multiple change messages in quick succession. To avoid
796 // refreshing multiple times (and to avoid waiting in the message thread)
797 // we set a timer to run in 500ms, which should leave enough time for all
798 // the messages to come through. Wrap so we don't set multiple timers
799 //
800 if (InterlockedCompareExchange((LONG *)&This->m_RefreshPending, TRUE, FALSE) == FALSE)
801 {
802 SetTimer(hwnd, REFRESH_TIMER, 500, NULL);
803 }
804 }
805 break;
806 }
807
808 case WM_TIMER:
809 {
810 if (wParam == REFRESH_TIMER)
811 {
812 // Schedule a refresh (this just creates a thread and returns)
813 This->RefreshView(This->m_DeviceView->GetCurrentView(), true);
814
815 // Cleanup the timer
816 KillTimer(hwnd, REFRESH_TIMER);
817
818 // Allow more change notifications
819 InterlockedExchange((LONG *)&This->m_RefreshPending, FALSE);
820 }
821 break;
822 }
823
824 case WM_ENTERMENULOOP:
825 {
826 This->UpdateStatusBar(true);
827 break;
828 }
829
830 case WM_EXITMENULOOP:
831 {
832 This->UpdateStatusBar(false);
833 break;
834 }
835
836 case WM_CLOSE:
837 {
838 // Destroy the main window
839 DestroyWindow(hwnd);
840 break;
841 }
842
843 case WM_ACTIVATE:
844 {
845 if (LOWORD(hwnd))
846 This->OnActivate();
847 break;
848 }
849
850 case WM_DESTROY:
851 {
852 // Call the destroy handler
853 RetCode = This->OnDestroy();
854 break;
855 }
856
857 default:
858 {
859 HandleDefaultMessage:
860 RetCode = DefWindowProc(hwnd, msg, wParam, lParam);
861 break;
862 }
863 }
864
865 return RetCode;
866 }