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