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