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(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 * @GroupBoxUpdateTitle 157 * 158 * Updates the title of the groupbox. 159 * 160 * @return 161 * Nothing. 162 * 163 */ 164 VOID GroupBoxUpdateTitle(VOID) 165 { 166 WCHAR szFormat[MAX_BUFFER]; 167 168 /* Format the string with the utility's name and set it to the listbox's title */ 169 StringCchPrintfW(szFormat, _countof(szFormat), Globals.szGrpBoxTitle, EntriesList[Globals.iSelectedIndex].szResource); 170 SetWindowTextW(GetDlgItem(Globals.hMainDlg, IDC_GROUPBOX), szFormat); 171 } 172 173 /** 174 * @UpdateUtilityState 175 * 176 * Checks the state of the given accessibility tool. 177 * 178 * @param[in] bUtilState 179 * State condition (boolean TRUE: started / FALSE: stopped). 180 * 181 * @return 182 * Nothing. 183 * 184 */ 185 VOID UpdateUtilityState(IN BOOL bUtilState) 186 { 187 Button_Enable(Globals.hDlgCtlStart, !bUtilState); 188 Button_Enable(Globals.hDlgCtlStop, bUtilState); 189 190 /* Update the groupbox's title based on the selected utility item */ 191 GroupBoxUpdateTitle(); 192 } 193 194 /** 195 * @ListBoxRefreshContents 196 * 197 * Handle the tasks on a periodic cycle. This function handles WM_TIMER message. 198 * 199 * @return 200 * Returns 0 to inform the system that WM_TIMER has been processed. 201 * 202 */ 203 INT ListBoxRefreshContents(VOID) 204 { 205 UINT i; 206 INT iItem; 207 BOOL bIsRunning; 208 WCHAR szFormat[MAX_BUFFER]; 209 210 /* Disable listbox redraw */ 211 SendMessageW(Globals.hListDlg, WM_SETREDRAW, FALSE, 0); 212 213 for (i = 0; i < _countof(EntriesList); ++i) 214 { 215 /* Check the utility's state */ 216 bIsRunning = IsProcessRunning(EntriesList[i].lpszProgram); 217 if (bIsRunning != EntriesList[i].bState) 218 { 219 /* The utility's state has changed, save it */ 220 EntriesList[i].bState = bIsRunning; 221 222 /* Update the corresponding item in the listbox */ 223 StringCchPrintfW(szFormat, _countof(szFormat), 224 (bIsRunning ? Globals.szRunning : Globals.szNotRunning), 225 EntriesList[i].szResource); 226 227 SendMessageW(Globals.hListDlg, LB_DELETESTRING, (LPARAM)i, 0); 228 iItem = SendMessageW(Globals.hListDlg, LB_INSERTSTRING, (LPARAM)i, (LPARAM)szFormat); 229 if (iItem != LB_ERR) 230 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]); 231 } 232 } 233 234 /* Re-enable listbox redraw */ 235 SendMessageW(Globals.hListDlg, WM_SETREDRAW, TRUE, 0); 236 237 /* 238 * Check the previously selected item. This will help us determine what 239 * item has been selected and set its focus selection back. Furthermore, check 240 * the state of each accessibility tool and enable/disable the buttons. 241 */ 242 SendMessageW(Globals.hListDlg, LB_SETCURSEL, (WPARAM)Globals.iSelectedIndex, 0); 243 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState); 244 245 return 0; 246 } 247 248 /** 249 * @DlgProc 250 * 251 * Main dialog application procedure function. 252 * 253 * @param[in] hDlg 254 * The handle object of the dialog. 255 * 256 * @param[in] Msg 257 * Message events (in unsigned int). 258 * 259 * @param[in] wParam 260 * Message parameter (in UINT_PTR). 261 * 262 * @param[in] lParam 263 * Message paramater (in LONG_PTR). 264 * 265 * @return 266 * Returns 0 to inform the system that the procedure has been handled. 267 * 268 */ 269 INT_PTR APIENTRY DlgProc( 270 IN HWND hDlg, 271 IN UINT Msg, 272 IN WPARAM wParam, 273 IN LPARAM lParam) 274 { 275 switch (Msg) 276 { 277 case WM_INITDIALOG: 278 DlgInitHandler(hDlg); 279 return TRUE; 280 281 case WM_CLOSE: 282 KillTimer(hDlg, Globals.iTimer); 283 DestroyIcon(Globals.hIcon); 284 EndDialog(hDlg, FALSE); 285 break; 286 287 case WM_COMMAND: 288 { 289 switch (LOWORD(wParam)) 290 { 291 case IDC_OK: 292 case IDC_CANCEL: 293 EndDialog(hDlg, FALSE); 294 break; 295 296 case IDC_LISTBOX: 297 { 298 switch (HIWORD(wParam)) 299 { 300 case LBN_SELCHANGE: 301 { 302 /* Retrieve the index of the current selected item */ 303 INT iIndex = SendMessageW(Globals.hListDlg, LB_GETCURSEL, 0, 0); 304 if ((iIndex == LB_ERR) || (iIndex >= _countof(EntriesList))) 305 break; 306 307 /* Assign the selected index and check the utility's state */ 308 Globals.iSelectedIndex = iIndex; 309 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState); 310 break; 311 } 312 break; 313 } 314 break; 315 } 316 317 case IDC_START: 318 LaunchProcess(EntriesList[Globals.iSelectedIndex].lpszProgram); 319 break; 320 321 case IDC_STOP: 322 CloseProcess(EntriesList[Globals.iSelectedIndex].lpszProgram); 323 break; 324 325 default: 326 break; 327 } 328 break; 329 } 330 331 case WM_TIMER: 332 ListBoxRefreshContents(); 333 return 0; 334 335 case WM_SYSCOMMAND: 336 { 337 switch (LOWORD(wParam)) 338 { 339 case IDM_ABOUT: 340 ShowAboutDlg(hDlg); 341 break; 342 } 343 break; 344 } 345 } 346 347 return 0; 348 } 349 350 /** 351 * @UManStartDlg 352 * 353 * Executes the dialog initialization mechanism and starts Utility Manager. 354 * The function is exported for use by the main process. 355 * 356 * @return 357 * Returns TRUE when the operation has succeeded, FALSE otherwise. 358 * 359 */ 360 BOOL WINAPI UManStartDlg(VOID) 361 { 362 HANDLE hMutex; 363 DWORD dwError; 364 INITCOMMONCONTROLSEX iccex; 365 366 /* Create a mutant object for the program. */ 367 hMutex = CreateMutexW(NULL, FALSE, L"Utilman"); 368 if (hMutex) 369 { 370 /* Check if there's already a mutex for the program */ 371 dwError = GetLastError(); 372 if (dwError == ERROR_ALREADY_EXISTS) 373 { 374 /* 375 The program's instance is already here. That means 376 the program is running and we should not set a new instance 377 and mutex object. 378 */ 379 CloseHandle(hMutex); 380 return FALSE; 381 } 382 } 383 384 /* Load the common controls for the program */ 385 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); 386 iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; 387 InitCommonControlsEx(&iccex); 388 389 LoadStringW(Globals.hInstance, IDS_RUNNING, 390 Globals.szRunning, _countof(Globals.szRunning)); 391 LoadStringW(Globals.hInstance, IDS_NOTRUNNING, 392 Globals.szNotRunning, _countof(Globals.szNotRunning)); 393 LoadStringW(Globals.hInstance, IDS_GROUPBOX_OPTIONS_TITLE, 394 Globals.szGrpBoxTitle, _countof(Globals.szGrpBoxTitle)); 395 396 /* Initialize the list of accessibility utilities */ 397 InitUtilsList(FALSE); 398 399 /* Create the dialog box of the program */ 400 DialogBoxW(Globals.hInstance, 401 MAKEINTRESOURCEW(IDD_MAIN_DIALOG), 402 GetDesktopWindow(), 403 DlgProc); 404 405 /* Delete the mutex */ 406 if (hMutex) 407 { 408 CloseHandle(hMutex); 409 } 410 411 return TRUE; 412 } 413 414 /** 415 * @DllMain 416 * 417 * Core routine of the Utility Manager's library. 418 * 419 * @param[in] hDllInstance 420 * The entry point instance of the library. 421 * 422 * @param[in] fdwReason 423 * The reason argument to indicate the motive DllMain 424 * is being called. 425 * 426 * @param[in] lpvReserved 427 * Reserved. 428 * 429 * @return 430 * Returns TRUE when main call initialization has succeeded, FALSE 431 * otherwise. 432 * 433 */ 434 BOOL WINAPI DllMain(IN HINSTANCE hDllInstance, 435 IN DWORD fdwReason, 436 IN LPVOID lpvReserved) 437 { 438 switch (fdwReason) 439 { 440 case DLL_PROCESS_ATTACH: 441 { 442 /* We don't care for DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications */ 443 DisableThreadLibraryCalls(hDllInstance); 444 445 /* Initialize the globals */ 446 ZeroMemory(&Globals, sizeof(Globals)); 447 Globals.hInstance = hDllInstance; 448 break; 449 } 450 451 case DLL_PROCESS_DETACH: 452 break; 453 } 454 455 return TRUE; 456 } 457