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 Bișoc George (fraizeraust99 at gmail dot com) 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 paramater (in LONG_PTR). 287 * 288 * @return 289 * Returns 0 to inform the system that the procedure has been handled. 290 * 291 */ 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 */ 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 */ 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