1 /*
2  * PROJECT:         ReactOS Utility Manager Resources DLL (UManDlg.dll)
3  * LICENSE:         GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:         Main DLL code file
5  * COPYRIGHT:       Copyright 2019-2020 George Bișoc (george.bisoc@reactos.org)
6  *                  Copyright 2019 Hermes Belusca-Maito
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "umandlg.h"
12 
13 /* GLOBALS ********************************************************************/
14 
15 UTILMAN_GLOBALS Globals;
16 
17 /* DECLARATIONS ***************************************************************/
18 
19 UTILMAN_STATE EntriesList[] =
20 {
21     {L"magnify.exe", IDS_MAGNIFIER, L"", FALSE},
22     {L"osk.exe", IDS_OSK, L"", FALSE}
23 };
24 
25 /* FUNCTIONS ******************************************************************/
26 
27 /**
28  * @InitUtilsList
29  *
30  * Initializes the list of accessibility utilities.
31  *
32  * @param[in]   bInitGui
33  *     Whether we are initializing the UI list (TRUE) or the internal array (FALSE).
34  *
35  * @return
36  *     Nothing.
37  */
InitUtilsList(IN BOOL bInitGui)38 VOID InitUtilsList(IN BOOL bInitGui)
39 {
40     UINT i;
41 
42     if (!bInitGui)
43     {
44         // TODO: Load the list dynamically from the registry key
45         // hklm\software\microsoft\windows nt\currentversion\accessibility
46 
47         /* Initialize the resource utility strings only once */
48         for (i = 0; i < _countof(EntriesList); ++i)
49         {
50             LoadStringW(Globals.hInstance, EntriesList[i].uNameId,
51                         EntriesList[i].szResource, _countof(EntriesList[i].szResource));
52 
53             EntriesList[i].bState = FALSE;
54         }
55     }
56     else
57     {
58         INT iItem;
59         BOOL bIsRunning;
60         WCHAR szFormat[MAX_BUFFER];
61 
62         /* Reset the listbox */
63         SendMessageW(Globals.hListDlg, LB_RESETCONTENT, 0, 0);
64 
65         /* Add the utilities in the listbox */
66         for (i = 0; i < _countof(EntriesList); ++i)
67         {
68             bIsRunning = IsProcessRunning(EntriesList[i].lpszProgram);
69             EntriesList[i].bState = bIsRunning;
70 
71             /* Load the string and append the utility's name to the format */
72             StringCchPrintfW(szFormat, _countof(szFormat),
73                              (bIsRunning ? Globals.szRunning : Globals.szNotRunning),
74                              EntriesList[i].szResource);
75 
76             /* Add the item in the listbox */
77             iItem = (INT)SendMessageW(Globals.hListDlg, LB_ADDSTRING, 0, (LPARAM)szFormat);
78             if (iItem != LB_ERR)
79                 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]);
80         }
81     }
82 }
83 
84 /**
85  * @DlgInitHandler
86  *
87  * Function which processes several operations for WM_INITDIALOG.
88  *
89  * @param[in]   hDlg
90  *     The handle object of the dialog.
91  *
92  * @return
93  *     TRUE to inform the system that WM_INITDIALOG has been processed and
94  *     that it should set the keyboard focus to the control.
95  *
96  */
DlgInitHandler(IN HWND hDlg)97 BOOL DlgInitHandler(IN HWND hDlg)
98 {
99     INT PosX, PosY;
100     RECT rc;
101     WCHAR szAboutDlg[MAX_BUFFER];
102     WCHAR szAppPath[MAX_BUFFER];
103     HMENU hSysMenu;
104 
105     /* Save the dialog handle */
106     Globals.hMainDlg = hDlg;
107 
108     /* Center the dialog on the screen */
109     GetWindowRect(hDlg, &rc);
110     PosX = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
111     PosY = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
112     SetWindowPos(hDlg, 0, PosX, PosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
113 
114     /* Extract the icon resource from the executable process */
115     GetModuleFileNameW(NULL, szAppPath, _countof(szAppPath));
116     Globals.hIcon = ExtractIconW(Globals.hInstance, szAppPath, 0);
117 
118     /* Set the icon within the dialog's title bar */
119     if (Globals.hIcon)
120     {
121         SendMessageW(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)Globals.hIcon);
122     }
123 
124     /* Retrieve the system menu and append the "About" menu item onto it */
125     hSysMenu = GetSystemMenu(hDlg, FALSE);
126     if (hSysMenu != NULL)
127     {
128         if (LoadStringW(Globals.hInstance, IDM_ABOUT, szAboutDlg, _countof(szAboutDlg)))
129         {
130             AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL);
131             AppendMenuW(hSysMenu, MF_STRING, IDM_ABOUT, szAboutDlg);
132         }
133     }
134 
135     /* Get the dialog items, specifically the dialog list box, the Start and Stop buttons */
136     Globals.hListDlg = GetDlgItem(hDlg, IDC_LISTBOX);
137     Globals.hDlgCtlStart = GetDlgItem(hDlg, IDC_START);
138     Globals.hDlgCtlStop = GetDlgItem(hDlg, IDC_STOP);
139 
140     /* Initialize the GUI listbox */
141     InitUtilsList(TRUE);
142 
143     /* Set the selection to the first item */
144     Globals.iSelectedIndex = 0;
145 
146     /* Refresh the list */
147     ListBoxRefreshContents();
148 
149     /* Create a timer, we'll use it to control the state of our items in the listbox */
150     Globals.iTimer = SetTimer(hDlg, 0, 400, NULL);
151 
152     return TRUE;
153 }
154 
155 /**
156  * @ShowAboutDlg
157  *
158  * Displays the Shell "About" dialog box.
159  *
160  * @param[in]   hDlgParent
161  *     A handle to the parent dialog window.
162  *
163  * @return
164  *     Nothing.
165  *
166  */
ShowAboutDlg(IN HWND hDlgParent)167 VOID ShowAboutDlg(IN HWND hDlgParent)
168 {
169     WCHAR szApp[MAX_BUFFER];
170     WCHAR szAuthors[MAX_BUFFER];
171 
172     LoadStringW(Globals.hInstance, IDS_APP_NAME, szApp, _countof(szApp));
173     LoadStringW(Globals.hInstance, IDS_AUTHORS, szAuthors, _countof(szAuthors));
174 
175     ShellAboutW(hDlgParent, szApp, szAuthors, Globals.hIcon);
176 }
177 
178 /**
179  * @GroupBoxUpdateTitle
180  *
181  * Updates the title of the groupbox.
182  *
183  * @return
184  *     Nothing.
185  *
186  */
GroupBoxUpdateTitle(VOID)187 VOID GroupBoxUpdateTitle(VOID)
188 {
189     WCHAR szFormat[MAX_BUFFER];
190 
191     /* Format the string with the utility's name and set it to the listbox's title */
192     StringCchPrintfW(szFormat, _countof(szFormat), Globals.szGrpBoxTitle, EntriesList[Globals.iSelectedIndex].szResource);
193     SetWindowTextW(GetDlgItem(Globals.hMainDlg, IDC_GROUPBOX), szFormat);
194 }
195 
196 /**
197  * @UpdateUtilityState
198  *
199  * Checks the state of the given accessibility tool.
200  *
201  * @param[in]   bUtilState
202  *     State condition (boolean TRUE: started / FALSE: stopped).
203  *
204  * @return
205  *     Nothing.
206  *
207  */
UpdateUtilityState(IN BOOL bUtilState)208 VOID UpdateUtilityState(IN BOOL bUtilState)
209 {
210     Button_Enable(Globals.hDlgCtlStart, !bUtilState);
211     Button_Enable(Globals.hDlgCtlStop,   bUtilState);
212 
213     /* Update the groupbox's title based on the selected utility item */
214     GroupBoxUpdateTitle();
215 }
216 
217 /**
218  * @ListBoxRefreshContents
219  *
220  * Handle the tasks on a periodic cycle. This function handles WM_TIMER message.
221  *
222  * @return
223  *     Returns 0 to inform the system that WM_TIMER has been processed.
224  *
225  */
ListBoxRefreshContents(VOID)226 INT ListBoxRefreshContents(VOID)
227 {
228     UINT i;
229     INT iItem;
230     BOOL bIsRunning;
231     WCHAR szFormat[MAX_BUFFER];
232 
233     /* Disable listbox redraw */
234     SendMessageW(Globals.hListDlg, WM_SETREDRAW, FALSE, 0);
235 
236     for (i = 0; i < _countof(EntriesList); ++i)
237     {
238         /* Check the utility's state */
239         bIsRunning = IsProcessRunning(EntriesList[i].lpszProgram);
240         if (bIsRunning != EntriesList[i].bState)
241         {
242             /* The utility's state has changed, save it */
243             EntriesList[i].bState = bIsRunning;
244 
245             /* Update the corresponding item in the listbox */
246             StringCchPrintfW(szFormat, _countof(szFormat),
247                              (bIsRunning ? Globals.szRunning : Globals.szNotRunning),
248                              EntriesList[i].szResource);
249 
250             SendMessageW(Globals.hListDlg, LB_DELETESTRING, (LPARAM)i, 0);
251             iItem = SendMessageW(Globals.hListDlg, LB_INSERTSTRING, (LPARAM)i, (LPARAM)szFormat);
252             if (iItem != LB_ERR)
253                 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]);
254         }
255     }
256 
257     /* Re-enable listbox redraw */
258     SendMessageW(Globals.hListDlg, WM_SETREDRAW, TRUE, 0);
259 
260     /*
261      * Check the previously selected item. This will help us determine what
262      * item has been selected and set its focus selection back. Furthermore, check
263      * the state of each accessibility tool and enable/disable the buttons.
264      */
265     SendMessageW(Globals.hListDlg, LB_SETCURSEL, (WPARAM)Globals.iSelectedIndex, 0);
266     UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
267 
268     return 0;
269 }
270 
271 /**
272  * @DlgProc
273  *
274  * Main dialog application procedure function.
275  *
276  * @param[in]   hDlg
277  *     The handle object of the dialog.
278  *
279  * @param[in]   Msg
280  *     Message events (in unsigned int).
281  *
282  * @param[in]   wParam
283  *     Message parameter (in UINT_PTR).
284  *
285  * @param[in]   lParam
286  *     Message parameter (in LONG_PTR).
287  *
288  * @return
289  *     Returns 0 to inform the system that the procedure has been handled.
290  *
291  */
DlgProc(IN HWND hDlg,IN UINT Msg,IN WPARAM wParam,IN LPARAM lParam)292 INT_PTR APIENTRY DlgProc(
293     IN HWND hDlg,
294     IN UINT Msg,
295     IN WPARAM wParam,
296     IN LPARAM lParam)
297 {
298     switch (Msg)
299     {
300         case WM_INITDIALOG:
301             DlgInitHandler(hDlg);
302             return TRUE;
303 
304         case WM_CLOSE:
305             KillTimer(hDlg, Globals.iTimer);
306             DestroyIcon(Globals.hIcon);
307             EndDialog(hDlg, FALSE);
308             break;
309 
310         case WM_COMMAND:
311         {
312             switch (LOWORD(wParam))
313             {
314                 case IDC_OK:
315                 case IDC_CANCEL:
316                     EndDialog(hDlg, FALSE);
317                     break;
318 
319                 case IDC_LISTBOX:
320                 {
321                     switch (HIWORD(wParam))
322                     {
323                         case LBN_SELCHANGE:
324                         {
325                             /* Retrieve the index of the current selected item */
326                             INT iIndex = SendMessageW(Globals.hListDlg, LB_GETCURSEL, 0, 0);
327                             if ((iIndex == LB_ERR) || (iIndex >= _countof(EntriesList)))
328                                 break;
329 
330                             /* Assign the selected index and check the utility's state */
331                             Globals.iSelectedIndex = iIndex;
332                             UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
333                             break;
334                         }
335                         break;
336                     }
337                     break;
338                 }
339 
340                 case IDC_START:
341                     LaunchProcess(EntriesList[Globals.iSelectedIndex].lpszProgram);
342                     break;
343 
344                 case IDC_STOP:
345                     CloseProcess(EntriesList[Globals.iSelectedIndex].lpszProgram);
346                     break;
347 
348                 default:
349                     break;
350             }
351             break;
352         }
353 
354         case WM_TIMER:
355             ListBoxRefreshContents();
356             return 0;
357 
358         case WM_SYSCOMMAND:
359         {
360             switch (LOWORD(wParam))
361             {
362                 case IDM_ABOUT:
363                     ShowAboutDlg(hDlg);
364                     break;
365             }
366             break;
367         }
368     }
369 
370     return 0;
371 }
372 
373 /**
374  * @UManStartDlg
375  *
376  * Executes the dialog initialization mechanism and starts Utility Manager.
377  * The function is exported for use by the main process.
378  *
379  * @return
380  *     Returns TRUE when the operation has succeeded, FALSE otherwise.
381  *
382  */
UManStartDlg(VOID)383 BOOL WINAPI UManStartDlg(VOID)
384 {
385     HANDLE hMutex;
386     DWORD dwError;
387     INITCOMMONCONTROLSEX iccex;
388 
389     /* Create a mutant object for the program. */
390     hMutex = CreateMutexW(NULL, FALSE, L"Utilman");
391     if (hMutex)
392     {
393         /* Check if there's already a mutex for the program */
394         dwError = GetLastError();
395         if (dwError == ERROR_ALREADY_EXISTS)
396         {
397             /*
398                 The program's instance is already here. That means
399                 the program is running and we should not set a new instance
400                 and mutex object.
401             */
402             CloseHandle(hMutex);
403             return FALSE;
404         }
405     }
406 
407     /* Load the common controls for the program */
408     iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
409     iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
410     InitCommonControlsEx(&iccex);
411 
412     LoadStringW(Globals.hInstance, IDS_RUNNING,
413                 Globals.szRunning, _countof(Globals.szRunning));
414     LoadStringW(Globals.hInstance, IDS_NOTRUNNING,
415                 Globals.szNotRunning, _countof(Globals.szNotRunning));
416     LoadStringW(Globals.hInstance, IDS_GROUPBOX_OPTIONS_TITLE,
417                 Globals.szGrpBoxTitle, _countof(Globals.szGrpBoxTitle));
418 
419     /* Initialize the list of accessibility utilities */
420     InitUtilsList(FALSE);
421 
422     /* Create the dialog box of the program */
423     DialogBoxW(Globals.hInstance,
424                MAKEINTRESOURCEW(IDD_MAIN_DIALOG),
425                GetDesktopWindow(),
426                DlgProc);
427 
428     /* Delete the mutex */
429     if (hMutex)
430     {
431         CloseHandle(hMutex);
432     }
433 
434     return TRUE;
435 }
436 
437 /**
438  * @DllMain
439  *
440  * Core routine of the Utility Manager's library.
441  *
442  * @param[in]   hDllInstance
443  *      The entry point instance of the library.
444  *
445  * @param[in]   fdwReason
446  *      The reason argument to indicate the motive DllMain
447  *      is being called.
448  *
449  * @param[in]   lpvReserved
450  *      Reserved.
451  *
452  * @return
453  *     Returns TRUE when main call initialization has succeeded, FALSE
454  *     otherwise.
455  *
456  */
DllMain(IN HINSTANCE hDllInstance,IN DWORD fdwReason,IN LPVOID lpvReserved)457 BOOL WINAPI DllMain(IN HINSTANCE hDllInstance,
458                     IN DWORD fdwReason,
459                     IN LPVOID lpvReserved)
460 {
461     switch (fdwReason)
462     {
463         case DLL_PROCESS_ATTACH:
464         {
465             /* We don't care for DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications */
466             DisableThreadLibraryCalls(hDllInstance);
467 
468             /* Initialize the globals */
469             ZeroMemory(&Globals, sizeof(Globals));
470             Globals.hInstance = hDllInstance;
471             break;
472         }
473 
474         case DLL_PROCESS_DETACH:
475             break;
476     }
477 
478     return TRUE;
479 }
480