1 /*
2  * PROJECT:     ReactOS Services
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/applications/mscutils/servman/mainwnd.c
5  * PURPOSE:     Main window message handler
6  * COPYRIGHT:   Copyright 2006-2017 Ged Murphy <gedmurphy@reactos.org>
7  *
8  */
9 
10 #include "precomp.h"
11 
12 #include <windowsx.h>
13 #include <shellapi.h>
14 
15 static const WCHAR szMainWndClass[] = L"ServManWndClass";
16 
17 /* Toolbar buttons */
18 static const TBBUTTON Buttons [] =
19 {   /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */
20     {TBICON_PROP,    ID_PROP,    TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0},    /* properties */
21     {TBICON_REFRESH, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},          /* refresh */
22     {TBICON_EXPORT,  ID_EXPORT,  TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},          /* export */
23 
24     /* Note: First item for a separator is its width in pixels */
25     {15, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                                  /* separator */
26 
27     {TBICON_CREATE,  ID_CREATE,  TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* create */
28     {TBICON_DELETE,  ID_DELETE,  TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* delete */
29 
30     {15, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                                  /* separator */
31 
32     {TBICON_START,   ID_START,   TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* start */
33     {TBICON_STOP,    ID_STOP,    TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* stop */
34     {TBICON_PAUSE,   ID_PAUSE,   TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* pause */
35     {TBICON_RESTART, ID_RESTART, TBSTATE_INDETERMINATE, BTNS_BUTTON, {0}, 0, 0 },   /* restart */
36 };
37 
38 
39 /* menu hints */
40 static const MENU_HINT MainMenuHintTable[] = {
41     /* File Menu */
42     {ID_EXPORT,    IDS_HINT_EXPORT},
43     {ID_EXIT,      IDS_HINT_EXIT},
44 
45     /* Action Menu */
46     {ID_CONNECT,  IDS_HINT_CONNECT},
47     {ID_START,    IDS_HINT_START},
48     {ID_STOP,     IDS_HINT_STOP},
49     {ID_PAUSE,    IDS_HINT_PAUSE},
50     {ID_RESUME,   IDS_HINT_RESUME},
51     {ID_RESTART,  IDS_HINT_RESTART},
52     {ID_REFRESH,  IDS_HINT_REFRESH},
53     {ID_EDIT,     IDS_HINT_EDIT},
54     {ID_CREATE,   IDS_HINT_CREATE},
55     {ID_DELETE,   IDS_HINT_DELETE},
56     {ID_PROP,     IDS_HINT_PROP},
57 
58     /* View menu */
59     {ID_VIEW_LARGE,   IDS_HINT_LARGE},
60     {ID_VIEW_SMALL,   IDS_HINT_SMALL},
61     {ID_VIEW_LIST,    IDS_HINT_LIST},
62     {ID_VIEW_DETAILS, IDS_HINT_DETAILS},
63     {ID_VIEW_CUST,    IDS_HINT_CUST},
64 
65     /* Help Menu */
66     {ID_HELP,     IDS_HINT_HELP},
67     {ID_ABOUT,    IDS_HINT_ABOUT}
68 };
69 /* system menu hints */
70 static const MENU_HINT SystemMenuHintTable[] = {
71     {SC_RESTORE,    IDS_HINT_SYS_RESTORE},
72     {SC_MOVE,       IDS_HINT_SYS_MOVE},
73     {SC_SIZE,       IDS_HINT_SYS_SIZE},
74     {SC_MINIMIZE,   IDS_HINT_SYS_MINIMIZE},
75     {SC_MAXIMIZE,   IDS_HINT_SYS_MAXIMIZE},
76     {SC_CLOSE,      IDS_HINT_SYS_CLOSE},
77 };
78 
79 
80 static BOOL
81 MainWndMenuHint(PMAIN_WND_INFO Info,
82                 WORD CmdId,
83                 const MENU_HINT *HintArray,
84                 DWORD HintsCount,
85                 UINT DefHintId)
86 {
87     BOOL Found = FALSE;
88     const MENU_HINT *LastHint;
89     UINT HintId = DefHintId;
90 
91     LastHint = HintArray + HintsCount;
92     while (HintArray != LastHint)
93     {
94         if (HintArray->CmdId == CmdId)
95         {
96             HintId = HintArray->HintId;
97             Found = TRUE;
98             break;
99         }
100         HintArray++;
101     }
102 
103     StatusBarLoadString(Info->hStatus,
104                         SB_SIMPLEID,
105                         hInstance,
106                         HintId);
107 
108     return Found;
109 }
110 
111 
112 static VOID
113 UpdateMainStatusBar(PMAIN_WND_INFO Info)
114 {
115     if (Info->hStatus != NULL)
116     {
117         SendMessage(Info->hStatus,
118                     SB_SIMPLE,
119                     (WPARAM)Info->bInMenuLoop,
120                     0);
121     }
122 }
123 
124 VOID
125 UpdateServiceCount(PMAIN_WND_INFO Info)
126 {
127     LPWSTR lpNumServices;
128 
129     if (AllocAndLoadString(&lpNumServices,
130                            hInstance,
131                            IDS_NUM_SERVICES))
132     {
133         WCHAR szNumServices[32];
134 
135         INT NumListedServ = ListView_GetItemCount(Info->hListView);
136 
137         _snwprintf(szNumServices,
138                    31,
139                    lpNumServices,
140                    NumListedServ);
141 
142         SendMessage(Info->hStatus,
143                     SB_SETTEXT,
144                     0,
145                     (LPARAM)szNumServices);
146 
147         LocalFree(lpNumServices);
148     }
149 }
150 
151 
152 VOID SetMenuAndButtonStates(PMAIN_WND_INFO Info)
153 {
154     HMENU hMainMenu;
155     UINT i;
156 
157     /* get handle to menu */
158     hMainMenu = GetMenu(Info->hMainWnd);
159 
160     /* set all to greyed */
161     for (i = ID_START; i <= ID_RESTART; i++)
162     {
163         EnableMenuItem(hMainMenu, i, MF_GRAYED);
164         EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), i, MF_GRAYED);
165         SendMessage(Info->hTool, TB_SETSTATE, i,
166                     (LPARAM)MAKELONG(TBSTATE_INDETERMINATE, 0));
167     }
168 
169     if (Info->SelectedItem != NO_ITEM_SELECTED)
170     {
171         LPQUERY_SERVICE_CONFIG lpServiceConfig = NULL;
172         DWORD Flags, State;
173 
174         /* allow user to delete service */
175         if (Info->bIsUserAnAdmin)
176         {
177             SendMessage(Info->hTool, TB_SETSTATE, ID_DELETE,
178                        (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
179             EnableMenuItem(hMainMenu, ID_DELETE, MF_ENABLED);
180             EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_DELETE, MF_ENABLED);
181         }
182 
183         Flags = Info->pCurrentService->ServiceStatusProcess.dwControlsAccepted;
184         State = Info->pCurrentService->ServiceStatusProcess.dwCurrentState;
185 
186         lpServiceConfig = GetServiceConfig(Info->pCurrentService->lpServiceName);
187 
188         if (lpServiceConfig && lpServiceConfig->dwStartType != SERVICE_DISABLED)
189         {
190             if (State == SERVICE_STOPPED)
191             {
192                 EnableMenuItem(hMainMenu, ID_START, MF_ENABLED);
193                 EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_START, MF_ENABLED);
194                 SendMessage(Info->hTool, TB_SETSTATE, ID_START,
195                        (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
196             }
197 
198             if ( (Flags & SERVICE_ACCEPT_STOP) && (State == SERVICE_RUNNING) )
199             {
200                 EnableMenuItem(hMainMenu, ID_RESTART, MF_ENABLED);
201                 EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_RESTART, MF_ENABLED);
202                 SendMessage(Info->hTool, TB_SETSTATE, ID_RESTART,
203                        (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
204             }
205         }
206 
207         if(lpServiceConfig)
208             HeapFree(GetProcessHeap(), 0, lpServiceConfig);
209 
210         if ( (Flags & SERVICE_ACCEPT_STOP) && (State == SERVICE_RUNNING) )
211         {
212             EnableMenuItem(hMainMenu, ID_STOP, MF_ENABLED);
213             EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_STOP, MF_ENABLED);
214             SendMessage(Info->hTool, TB_SETSTATE, ID_STOP,
215                    (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
216         }
217 
218         if ( (Flags & SERVICE_ACCEPT_PAUSE_CONTINUE) && (State == SERVICE_RUNNING) )
219         {
220             EnableMenuItem(hMainMenu, ID_PAUSE, MF_ENABLED);
221             EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_PAUSE, MF_ENABLED);
222             SendMessage(Info->hTool, TB_SETSTATE, ID_PAUSE,
223                    (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
224         }
225     }
226     else
227     {
228         /* disable tools which rely on a selected service */
229         EnableMenuItem(hMainMenu, ID_PROP, MF_GRAYED);
230         EnableMenuItem(hMainMenu, ID_DELETE, MF_GRAYED);
231         EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_PROP, MF_GRAYED);
232         EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0), ID_DELETE, MF_GRAYED);
233         SendMessage(Info->hTool, TB_SETSTATE, ID_PROP,
234                    (LPARAM)MAKELONG(TBSTATE_INDETERMINATE, 0));
235         SendMessage(Info->hTool, TB_SETSTATE, ID_DELETE,
236                    (LPARAM)MAKELONG(TBSTATE_INDETERMINATE, 0));
237     }
238 
239 }
240 
241 
242 static INT CALLBACK
243 CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
244 {
245     PMAIN_WND_INFO Info = (PMAIN_WND_INFO)lParamSort;
246     WCHAR Item1[256], Item2[256];
247 
248     ListView_GetItemText(Info->hListView, lParam1, Info->SortSelection, Item1, sizeof(Item1) / sizeof(WCHAR));
249     ListView_GetItemText(Info->hListView, lParam2, Info->SortSelection, Item2, sizeof(Item2) / sizeof(WCHAR));
250 
251     return wcscmp(Item1, Item2) * Info->SortDirection;
252 }
253 
254 
255 static BOOL
256 pCreateToolbar(PMAIN_WND_INFO Info)
257 {
258     INT numButtons = sizeof(Buttons) / sizeof(Buttons[0]);
259 
260     Info->hTool = CreateWindowEx(0,
261                                  TOOLBARCLASSNAME,
262                                  NULL,
263                                  WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS,
264                                  0, 0, 0, 0,
265                                  Info->hMainWnd,
266                                  0,
267                                  hInstance,
268                                  NULL);
269     if(Info->hTool != NULL)
270     {
271         HIMAGELIST hImageList;
272 
273         SendMessage(Info->hTool,
274                     TB_SETEXTENDEDSTYLE,
275                     0,
276                     TBSTYLE_EX_HIDECLIPPEDBUTTONS);
277 
278         SendMessage(Info->hTool,
279                     TB_BUTTONSTRUCTSIZE,
280                     sizeof(Buttons[0]),
281                     0);
282 
283         hImageList = InitImageList(IDB_PROP,
284                                    IDB_RESTART,
285                                    GetSystemMetrics(SM_CXSMICON),
286                                    GetSystemMetrics(SM_CXSMICON),
287                                    IMAGE_BITMAP);
288         if (hImageList == NULL)
289             return FALSE;
290 
291         ImageList_Destroy((HIMAGELIST)SendMessage(Info->hTool,
292                                                   TB_SETIMAGELIST,
293                                                   0,
294                                                   (LPARAM)hImageList));
295 
296         SendMessage(Info->hTool,
297                     TB_ADDBUTTONS,
298                     numButtons,
299                     (LPARAM)Buttons);
300 
301         return TRUE;
302     }
303 
304     return FALSE;
305 }
306 
307 static BOOL
308 CreateStatusBar(PMAIN_WND_INFO Info)
309 {
310     INT StatWidths[] = {130, -1}; /* widths of status bar */
311 
312     Info->hStatus = CreateWindowEx(0,
313                                    STATUSCLASSNAME,
314                                    NULL,
315                                    WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
316                                    0, 0, 0, 0,
317                                    Info->hMainWnd,
318                                    (HMENU)IDC_STATUSBAR,
319                                    hInstance,
320                                    NULL);
321     if(Info->hStatus == NULL)
322         return FALSE;
323 
324     SendMessage(Info->hStatus,
325                 SB_SETPARTS,
326                 sizeof(StatWidths) / sizeof(INT),
327                 (LPARAM)StatWidths);
328 
329     return TRUE;
330 }
331 
332 
333 static BOOL
334 InitMainWnd(PMAIN_WND_INFO Info)
335 {
336     if (!pCreateToolbar(Info))
337     {
338         DisplayString(L"error creating toolbar");
339         return FALSE;
340     }
341 
342     if (!CreateListView(Info))
343     {
344         DisplayString(L"error creating list view");
345         return FALSE;
346     }
347 
348     if (!CreateStatusBar(Info))
349         DisplayString(L"error creating status bar");
350 
351     /* Create Popup Menu */
352     Info->hShortcutMenu = LoadMenu(hInstance,
353                                    MAKEINTRESOURCE(IDR_POPUP));
354 
355     Info->bIsUserAnAdmin = TRUE;// IsUserAnAdmin();
356     if (Info->bIsUserAnAdmin)
357     {
358         HMENU hMainMenu = GetMenu(Info->hMainWnd);
359 
360         SendMessage(Info->hTool,
361                     TB_SETSTATE,
362                     ID_CREATE,
363                     (LPARAM)MAKELONG(TBSTATE_ENABLED, 0));
364         if (hMainMenu)
365         {
366             EnableMenuItem(hMainMenu,
367                            ID_CREATE,
368                            MF_ENABLED);
369         }
370         EnableMenuItem(GetSubMenu(Info->hShortcutMenu, 0),
371                        ID_CREATE,
372                        MF_ENABLED);
373     }
374 
375     return TRUE;
376 }
377 
378 static VOID
379 MainWndCommand(PMAIN_WND_INFO Info,
380                WORD CmdId,
381                HWND hControl)
382 {
383     WCHAR szAppName[256];
384     WCHAR szAppAuthors[256];
385     HICON hIcon;
386 
387     UNREFERENCED_PARAMETER(hControl);
388 
389     switch (CmdId)
390     {
391         case ID_PROP:
392         {
393             if (Info->SelectedItem != NO_ITEM_SELECTED)
394             {
395                 Info->bDlgOpen = TRUE;
396                 OpenPropSheet(Info);
397                 Info->bDlgOpen = FALSE;
398                 SetMenuAndButtonStates(Info);
399             }
400         }
401         break;
402 
403         case ID_REFRESH:
404         {
405             RefreshServiceList(Info);
406             Info->SelectedItem = NO_ITEM_SELECTED;
407 
408             /* disable menus and buttons */
409             SetMenuAndButtonStates(Info);
410 
411             /* clear the service in the status bar */
412             SendMessage(Info->hStatus,
413                         SB_SETTEXT,
414                         1,
415                         L'\0');
416         }
417         break;
418 
419         case ID_EXPORT:
420         {
421             ExportFile(Info);
422             SetFocus(Info->hListView);
423         }
424         break;
425 
426         case ID_CREATE:
427         {
428             INT ret;
429 
430             ret = DialogBoxParam(hInstance,
431                                  MAKEINTRESOURCE(IDD_DLG_CREATE),
432                                  Info->hMainWnd,
433                                  CreateDialogProc,
434                                  (LPARAM)Info);
435             if (ret == IDOK)
436                 RefreshServiceList(Info);
437 
438             SetFocus(Info->hListView);
439         }
440         break;
441 
442         case ID_DELETE:
443         {
444             if (Info->pCurrentService->ServiceStatusProcess.dwCurrentState != SERVICE_RUNNING)
445             {
446                 DialogBoxParam(hInstance,
447                                MAKEINTRESOURCE(IDD_DLG_DELETE),
448                                Info->hMainWnd,
449                                DeleteDialogProc,
450                                (LPARAM)Info);
451             }
452             else
453             {
454                 WCHAR Buf[60];
455                 LoadString(hInstance,
456                            IDS_DELETE_STOP,
457                            Buf,
458                            sizeof(Buf) / sizeof(WCHAR));
459                 DisplayString(Buf);
460             }
461 
462             SetFocus(Info->hListView);
463 
464         }
465         break;
466 
467         case ID_START:
468         {
469             RunActionWithProgress(Info->hMainWnd,
470                                   Info->pCurrentService->lpServiceName,
471                                   Info->pCurrentService->lpDisplayName,
472                                   ACTION_START,
473                                   NULL); //FIXME: Add start params
474 
475             UpdateServiceStatus(Info->pCurrentService);
476             ChangeListViewText(Info, Info->pCurrentService, LVSTATUS);
477             SetMenuAndButtonStates(Info);
478             SetFocus(Info->hListView);
479 
480         }
481         break;
482 
483         case ID_STOP:
484             RunActionWithProgress(Info->hMainWnd,
485                                   Info->pCurrentService->lpServiceName,
486                                   Info->pCurrentService->lpDisplayName,
487                                   ACTION_STOP,
488                                   NULL);
489 
490             UpdateServiceStatus(Info->pCurrentService);
491             ChangeListViewText(Info, Info->pCurrentService, LVSTATUS);
492             SetMenuAndButtonStates(Info);
493             SetFocus(Info->hListView);
494 
495         break;
496 
497         case ID_PAUSE:
498             RunActionWithProgress(Info->hMainWnd,
499                                   Info->pCurrentService->lpServiceName,
500                                   Info->pCurrentService->lpDisplayName,
501                                   ACTION_PAUSE,
502                                   NULL);
503 
504             UpdateServiceStatus(Info->pCurrentService);
505             ChangeListViewText(Info, Info->pCurrentService, LVSTATUS);
506             SetMenuAndButtonStates(Info);
507             SetFocus(Info->hListView);
508         break;
509 
510         case ID_RESUME:
511             RunActionWithProgress(Info->hMainWnd,
512                                   Info->pCurrentService->lpServiceName,
513                                   Info->pCurrentService->lpDisplayName,
514                                   ACTION_RESUME,
515                                   NULL);
516 
517             UpdateServiceStatus(Info->pCurrentService);
518             ChangeListViewText(Info, Info->pCurrentService, LVSTATUS);
519             SetMenuAndButtonStates(Info);
520             SetFocus(Info->hListView);
521         break;
522 
523         case ID_RESTART:
524             RunActionWithProgress(Info->hMainWnd,
525                                   Info->pCurrentService->lpServiceName,
526                                   Info->pCurrentService->lpDisplayName,
527                                   ACTION_RESTART,
528                                   NULL);
529 
530             UpdateServiceStatus(Info->pCurrentService);
531             ChangeListViewText(Info, Info->pCurrentService, LVSTATUS);
532             SetMenuAndButtonStates(Info);
533             SetFocus(Info->hListView);
534         break;
535 
536         case ID_HELP:
537             MessageBoxW(NULL,
538                         L"Help is not yet implemented\n",
539                         L"Note!",
540                         MB_OK | MB_ICONINFORMATION);
541             SetFocus(Info->hListView);
542         break;
543 
544         case ID_EXIT:
545             PostMessage(Info->hMainWnd,
546                         WM_CLOSE,
547                         0,
548                         0);
549         break;
550 
551         case ID_VIEW_LARGE:
552             SetListViewStyle(Info->hListView, LVS_ICON);
553             ListView_Arrange(Info->hListView, LVA_DEFAULT);
554 
555             CheckMenuRadioItem(GetMenu(Info->hMainWnd),
556                                ID_VIEW_LARGE,
557                                ID_VIEW_DETAILS,
558                                ID_VIEW_LARGE,
559                                MF_BYCOMMAND);
560         break;
561 
562         case ID_VIEW_SMALL:
563             SetListViewStyle(Info->hListView, LVS_SMALLICON);
564             ListView_Arrange(Info->hListView, LVA_DEFAULT);
565 
566             CheckMenuRadioItem(GetMenu(Info->hMainWnd),
567                                ID_VIEW_LARGE,
568                                ID_VIEW_DETAILS,
569                                ID_VIEW_SMALL,
570                                MF_BYCOMMAND);
571         break;
572 
573         case ID_VIEW_LIST:
574             SetListViewStyle(Info->hListView,
575                              LVS_LIST);
576             CheckMenuRadioItem(GetMenu(Info->hMainWnd),
577                                ID_VIEW_LARGE,
578                                ID_VIEW_DETAILS,
579                                ID_VIEW_LIST,
580                                MF_BYCOMMAND);
581         break;
582 
583         case ID_VIEW_DETAILS:
584             SetListViewStyle(Info->hListView,
585                              LVS_REPORT);
586             CheckMenuRadioItem(GetMenu(Info->hMainWnd),
587                                ID_VIEW_LARGE,
588                                ID_VIEW_DETAILS,
589                                ID_VIEW_DETAILS,
590                                MF_BYCOMMAND);
591         break;
592 
593         case ID_VIEW_CUST:
594         break;
595 
596         case ID_ABOUT:
597             LoadStringW(hInstance, IDS_APPNAME, szAppName, _countof(szAppName));
598             LoadStringW(hInstance, IDS_APPAUTHORS, szAppAuthors, _countof(szAppAuthors));
599 
600             hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_SM_ICON));
601             ShellAboutW(Info->hMainWnd, szAppName, szAppAuthors, hIcon);
602             DestroyIcon(hIcon);
603         break;
604 
605     }
606 }
607 
608 
609 static VOID CALLBACK
610 MainWndResize(PMAIN_WND_INFO Info,
611               WORD cx,
612               WORD cy)
613 {
614     RECT rcClient, rcTool, rcStatus;
615     int lvHeight, iToolHeight, iStatusHeight;
616 
617     /* Size toolbar and get height */
618     SendMessage(Info->hTool, TB_AUTOSIZE, 0, 0);
619     GetWindowRect(Info->hTool, &rcTool);
620     iToolHeight = rcTool.bottom - rcTool.top;
621 
622     /* Size status bar and get height */
623     SendMessage(Info->hStatus, WM_SIZE, 0, 0);
624     GetWindowRect(Info->hStatus, &rcStatus);
625     iStatusHeight = rcStatus.bottom - rcStatus.top;
626 
627     /* Calculate remaining height and size list view */
628     GetClientRect(Info->hMainWnd, &rcClient);
629     lvHeight = rcClient.bottom - iToolHeight - iStatusHeight;
630     SetWindowPos(Info->hListView,
631                  NULL,
632                  0,
633                  iToolHeight,
634                  rcClient.right,
635                  lvHeight,
636                  SWP_NOZORDER);
637 }
638 
639 
640 static LRESULT CALLBACK
641 MainWndProc(HWND hwnd,
642             UINT msg,
643             WPARAM wParam,
644             LPARAM lParam)
645 {
646     PMAIN_WND_INFO Info;
647     LRESULT Ret = 0;
648 
649     /* Get the window context */
650     Info = (PMAIN_WND_INFO)GetWindowLongPtr(hwnd,
651                                             GWLP_USERDATA);
652     if (Info == NULL && msg != WM_CREATE)
653     {
654         goto HandleDefaultMessage;
655     }
656 
657     switch(msg)
658     {
659         case WM_CREATE:
660         {
661             Info = (PMAIN_WND_INFO)(((LPCREATESTRUCT)lParam)->lpCreateParams);
662 
663             /* Initialize the main window context */
664             Info->hMainWnd = hwnd;
665             Info->SelectedItem = NO_ITEM_SELECTED;
666 
667             SetWindowLongPtr(hwnd,
668                              GWLP_USERDATA,
669                              (LONG_PTR)Info);
670 
671             if (!InitMainWnd(Info))
672                 return -1;
673 
674             /* Fill the list-view before showing the main window */
675             RefreshServiceList(Info);
676 
677             /* Show the window */
678             ShowWindow(hwnd,
679                        Info->nCmdShow);
680 
681             SetFocus(Info->hListView);
682         }
683         break;
684 
685         case WM_SIZE:
686         {
687             MainWndResize(Info,
688                           LOWORD(lParam),
689                           HIWORD(lParam));
690         }
691         break;
692 
693         case WM_NOTIFY:
694         {
695             LPNMHDR pnmhdr = (LPNMHDR)lParam;
696 
697             switch (pnmhdr->code)
698             {
699                 case NM_DBLCLK:
700                 {
701                     POINT pt;
702                     RECT rect;
703 
704                     GetCursorPos(&pt);
705                     GetWindowRect(Info->hListView, &rect);
706 
707                     if (PtInRect(&rect, pt))
708                     {
709                         SendMessage(hwnd,
710                                     WM_COMMAND,
711                                     //ID_PROP,
712                                     MAKEWPARAM((WORD)ID_PROP, (WORD)0),
713                                     0);
714                     }
715 
716                     //OpenPropSheet(Info);
717                 }
718                 break;
719 
720                 case NM_RETURN:
721                 {
722                     SendMessage(hwnd,
723                                 WM_COMMAND,
724                                 //ID_PROP,
725                                 MAKEWPARAM((WORD)ID_PROP, (WORD)0),
726                                 0);
727                 }
728                 break;
729 
730                 case LVN_COLUMNCLICK:
731                 {
732                     LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
733                     HDITEM       hdi;
734 
735                     /* get pending sort direction for clicked column */
736                     hdi.mask = HDI_LPARAM;
737                     (void)Header_GetItem(Info->hHeader, pnmv->iSubItem, &hdi);
738 
739                     /* get new sort parameters */
740                     Info->SortSelection = pnmv->iSubItem;
741                     Info->SortDirection = hdi.lParam;
742 
743                     /* set new sort direction and save */
744                     hdi.lParam = (hdi.lParam == ORD_ASCENDING) ?
745                                  ORD_DESCENDING : ORD_ASCENDING;
746 
747                     (void)Header_SetItem(Info->hHeader, pnmv->iSubItem, &hdi);
748 
749                     (void)ListView_SortItemsEx(Info->hListView,
750                                                CompareFunc,
751                                                (LPARAM)Info);
752                 }
753                 break;
754                 case LVN_ITEMCHANGED:
755                 {
756                     LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
757 
758                     if (pnmv->uNewState != 0)
759                     {
760                         ListViewSelectionChanged(Info, pnmv);
761                         SetMenuAndButtonStates(Info);
762                     }
763                 }
764                 break;
765 
766                 case TTN_GETDISPINFO:
767                 {
768                     LPTOOLTIPTEXT lpttt;
769                     UINT idButton;
770 
771                     lpttt = (LPTOOLTIPTEXT)lParam;
772 
773                     /* Specify the resource identifier of the descriptive
774                      * text for the given button. */
775                     idButton = (UINT)lpttt->hdr.idFrom;
776                     switch (idButton)
777                     {
778                         case ID_PROP:
779                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_PROP);
780                         break;
781 
782                         case ID_REFRESH:
783                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_REFRESH);
784                         break;
785 
786                         case ID_EXPORT:
787                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_EXPORT);
788                         break;
789 
790                         case ID_CREATE:
791                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_CREATE);
792                         break;
793 
794                         case ID_DELETE:
795                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_DELETE);
796                         break;
797 
798                         case ID_START:
799                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_START);
800                         break;
801 
802                         case ID_STOP:
803                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_STOP);
804                         break;
805 
806                         case ID_PAUSE:
807                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_PAUSE);
808                         break;
809 
810                         case ID_RESTART:
811                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_RESTART);
812                         break;
813                     }
814                 }
815                 break;
816             }
817         }
818         break;
819 
820         case WM_CONTEXTMENU:
821             {
822                 POINT pt;
823                 RECT lvRect;
824 
825                 INT xPos = GET_X_LPARAM(lParam);
826                 INT yPos = GET_Y_LPARAM(lParam);
827 
828                 GetCursorPos(&pt);
829 
830                 /* display popup when cursor is in the list view */
831                 GetWindowRect(Info->hListView, &lvRect);
832                 if (PtInRect(&lvRect, pt))
833                 {
834                     TrackPopupMenuEx(GetSubMenu(Info->hShortcutMenu, 0),
835                                      TPM_RIGHTBUTTON,
836                                      xPos,
837                                      yPos,
838                                      Info->hMainWnd,
839                                      NULL);
840                 }
841             }
842         break;
843 
844         case WM_COMMAND:
845         {
846             MainWndCommand(Info,
847                            LOWORD(wParam),
848                            (HWND)lParam);
849             goto HandleDefaultMessage;
850         }
851 
852         case WM_MENUSELECT:
853         {
854             if (Info->hStatus != NULL)
855             {
856                 if (!MainWndMenuHint(Info,
857                                      LOWORD(wParam),
858                                      MainMenuHintTable,
859                                      sizeof(MainMenuHintTable) / sizeof(MainMenuHintTable[0]),
860                                      IDS_HINT_BLANK))
861                 {
862                     MainWndMenuHint(Info,
863                                     LOWORD(wParam),
864                                     SystemMenuHintTable,
865                                     sizeof(SystemMenuHintTable) / sizeof(SystemMenuHintTable[0]),
866                                     IDS_HINT_BLANK);
867                 }
868             }
869         }
870         break;
871 
872         case WM_ENTERMENULOOP:
873         {
874             Info->bInMenuLoop = TRUE;
875             UpdateMainStatusBar(Info);
876             break;
877         }
878 
879         case WM_EXITMENULOOP:
880         {
881             Info->bInMenuLoop = FALSE;
882             UpdateMainStatusBar(Info);
883             break;
884         }
885 
886         case WM_CLOSE:
887         {
888             FreeServiceList(Info);
889             DestroyMenu(Info->hShortcutMenu);
890             DestroyWindow(hwnd);
891         }
892         break;
893 
894         case WM_DESTROY:
895         {
896             HeapFree(ProcessHeap,
897                      0,
898                      Info);
899             SetWindowLongPtr(hwnd,
900                              GWLP_USERDATA,
901                              0);
902 
903             PostQuitMessage(0);
904         }
905         break;
906 
907         default:
908         {
909 HandleDefaultMessage:
910 
911             Ret = DefWindowProc(hwnd,
912                                 msg,
913                                 wParam,
914                                 lParam);
915         }
916         break;
917     }
918 
919     return Ret;
920 }
921 
922 
923 
924 HWND
925 CreateMainWindow(LPCTSTR lpCaption,
926                  int nCmdShow)
927 {
928     PMAIN_WND_INFO Info;
929     HWND hMainWnd = NULL;
930 
931     Info = (MAIN_WND_INFO*) HeapAlloc(ProcessHeap,
932                      HEAP_ZERO_MEMORY,
933                      sizeof(MAIN_WND_INFO));
934 
935     if (Info != NULL)
936     {
937         Info->nCmdShow = nCmdShow;
938 
939         hMainWnd = CreateWindowEx(WS_EX_WINDOWEDGE,
940                                   szMainWndClass,
941                                   lpCaption,
942                                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
943                                   CW_USEDEFAULT,
944                                   CW_USEDEFAULT,
945                                   680,
946                                   450,
947                                   NULL,
948                                   NULL,
949                                   hInstance,
950                                   Info);
951         if (hMainWnd == NULL)
952         {
953             //int ret;
954             //ret = GetLastError();
955             GetError();
956             HeapFree(ProcessHeap,
957                      0,
958                      Info);
959         }
960     }
961 
962     return hMainWnd;
963 }
964 
965 BOOL
966 InitMainWindowImpl(VOID)
967 {
968     WNDCLASSEX wc = {0};
969 
970     wc.cbSize = sizeof(WNDCLASSEX);
971     wc.lpfnWndProc = MainWndProc;
972     wc.hInstance = hInstance;
973     wc.hIcon = LoadIcon(hInstance,
974                         MAKEINTRESOURCE(IDI_SM_ICON));
975     wc.hCursor = LoadCursor(NULL,
976                             IDC_ARROW);
977     wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
978     wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
979     wc.lpszClassName = szMainWndClass;
980     wc.hIconSm = (HICON)LoadImage(hInstance,
981                                   MAKEINTRESOURCE(IDI_SM_ICON),
982                                   IMAGE_ICON,
983                                   16,
984                                   16,
985                                   LR_SHARED);
986 
987     return RegisterClassEx(&wc) != (ATOM)0;
988 }
989 
990 
991 VOID
992 UninitMainWindowImpl(VOID)
993 {
994     UnregisterClass(szMainWndClass,
995                     hInstance);
996 }
997