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                                   GetSystemMetrics(SM_CXSMICON),
159                                   GetSystemMetrics(SM_CYSMICON),
160                                   0);
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 }
867