diff --git a/base/applications/CMakeLists.txt b/base/applications/CMakeLists.txt index 87cbdbf5e17..74ddde5815c 100644 --- a/base/applications/CMakeLists.txt +++ b/base/applications/CMakeLists.txt @@ -39,6 +39,7 @@ add_subdirectory(shutdown) add_subdirectory(sndrec32) add_subdirectory(sndvol32) add_subdirectory(taskmgr) +add_subdirectory(utilman) add_subdirectory(winhlp32) add_subdirectory(winver) add_subdirectory(wordpad) diff --git a/base/applications/utilman/CMakeLists.txt b/base/applications/utilman/CMakeLists.txt new file mode 100644 index 00000000000..90a909e9dd0 --- /dev/null +++ b/base/applications/utilman/CMakeLists.txt @@ -0,0 +1,13 @@ + +list(APPEND SOURCE + dialog.c + process.c + about.c + precomp.h) + +add_rc_deps(utilman.rc ${CMAKE_CURRENT_SOURCE_DIR}/res/utilman.ico) +add_executable(utilman ${SOURCE} utilman.rc) +set_module_type(utilman win32gui UNICODE) +add_importlibs(utilman comdlg32 user32 gdi32 advapi32 shell32 comctl32 msvcrt kernel32 ntdll) +add_pch(utilman precomp.h SOURCE) +add_cd_file(TARGET utilman DESTINATION reactos/system32 FOR all) diff --git a/base/applications/utilman/NOTES.txt b/base/applications/utilman/NOTES.txt new file mode 100644 index 00000000000..595c089ec1a --- /dev/null +++ b/base/applications/utilman/NOTES.txt @@ -0,0 +1,11 @@ +ReactOS Accessibility Utility Manager +======================================= + +Currently the application can manage the state of the accessibility tools however there are some things which needs to be DONE in order to consider the program as fully complete. The following are: + +- The warning dialog is not implemented YET +- The program can only control the launch/stopping states of accessibility tools, the code which manages the options for each utility is not implemented YET +- Function helper which catches the Windows logo + U keys is not implemented YET +- Registry configuration saver/loader is not implemented YET (XP's Utility Manager creates a key on HKEY_CURRENT_USER\Software\Microsoft with the name "Utility Manager" although the rest of other configuration values for utility options are stored elsewhere and I don't know exactly where are those) +- XP's and Server 2003's main Utility Manager implementation is compiled in a DLL (+ an executable which loads the DLL in question) whereas our Utility Manager is compiled as a whole executable +- On Windows Vista and later, it complains for required elevation to open a process. We need to improve our process creation code and discard ShellExecuteW() \ No newline at end of file diff --git a/base/applications/utilman/about.c b/base/applications/utilman/about.c new file mode 100644 index 00000000000..805fde451f9 --- /dev/null +++ b/base/applications/utilman/about.c @@ -0,0 +1,93 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: About dialog file + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +/* INCLUDES *******************************************************************/ + +#include "precomp.h" + +/* GLOBALS ********************************************************************/ + +UTILMAN_GLOBALS Globals; + +/* FUNCTIONS ******************************************************************/ + +/** + * @AboutDlgProc + * + * "About" dialog procedure. + * + * @param hDlg + * The handle object of the dialog. + * + * @param Msg + * Message events (in unsigned int). + * + * @param wParam + * Message parameter (in UINT_PTR). + * + * @param lParam + * Message paramater (in LONG_PTR). + * + * @return + * Return TRUE if the dialog processed messages, + * FALSE otherwise. + * + */ +INT_PTR CALLBACK AboutDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + switch (Msg) + { + case WM_INITDIALOG: + { + Globals.hIcon = LoadImageW(Globals.hInstance, + MAKEINTRESOURCEW(IDI_ICON_UTILMAN), + IMAGE_ICON, + 0, + 0, + LR_DEFAULTSIZE); + + /* Set the icon within the dialog's title bar */ + if (Globals.hIcon) + { + SendMessageW(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)Globals.hIcon); + } + + return TRUE; + } + + case WM_COMMAND: + { + case IDOK: + case IDCANCEL: + DestroyIcon(Globals.hIcon); + EndDialog(hDlg, FALSE); + break; + } + } + + return FALSE; +} + +/** + * @ShowAboutDlg + * + * Display the "About" dialog. + * + * @param hDlgParent + * The handle object of the parent dialog. + * + * @return + * Nothing. + */ +VOID ShowAboutDlg(HWND hDlgParent) +{ + /* Display the "About" dialog when the user clicks on the "About" menu item */ + DialogBoxW(Globals.hInstance, + MAKEINTRESOURCEW(IDD_ABOUT_DIALOG), + hDlgParent, + AboutDlgProc); +} diff --git a/base/applications/utilman/dialog.c b/base/applications/utilman/dialog.c new file mode 100644 index 00000000000..4ce74bfa7a6 --- /dev/null +++ b/base/applications/utilman/dialog.c @@ -0,0 +1,438 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Main dialog code file + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + * Copyright 2019 Hermes Belusca-Maito + */ + +/* INCLUDES *******************************************************************/ + +#include "precomp.h" + +/* GLOBALS ********************************************************************/ + +UTILMAN_GLOBALS Globals; + +/* DECLARATIONS ***************************************************************/ + +UTILMAN_STATE EntriesList[] = +{ + {L"magnify.exe", IDS_MAGNIFIER, L"", FALSE}, + {L"osk.exe", IDS_OSK, L"", FALSE} +}; + +/* FUNCTIONS ******************************************************************/ + +/** + * @InitUtilsList + * + * Initializes the list of accessibility utilities. + * + * @param[in] bInitGui + * Whether we are initializing the UI list (TRUE) or the internal array (FALSE). + * + * @return + * Nothing. + */ +VOID InitUtilsList(BOOL bInitGui) +{ + UINT i; + + if (!bInitGui) + { + // TODO: Load the list dynamically from the registry key + // hklm\software\microsoft\windows nt\currentversion\accessibility + + /* Initialize the resource utility strings only once */ + for (i = 0; i < _countof(EntriesList); ++i) + { + LoadStringW(Globals.hInstance, EntriesList[i].uNameId, + EntriesList[i].szResource, _countof(EntriesList[i].szResource)); + + EntriesList[i].bState = FALSE; + } + } + else + { + INT iItem; + BOOL bIsRunning; + WCHAR szFormat[MAX_BUFFER]; + + /* Reset the listbox */ + SendMessageW(Globals.hListDlg, LB_RESETCONTENT, 0, 0); + + /* Add the utilities in the listbox */ + for (i = 0; i < _countof(EntriesList); ++i) + { + bIsRunning = IsProcessRunning(EntriesList[i].lpProgram); + EntriesList[i].bState = bIsRunning; + + /* Load the string and append the utility's name to the format */ + StringCchPrintfW(szFormat, _countof(szFormat), + (bIsRunning ? Globals.szRunning : Globals.szNotRunning), + EntriesList[i].szResource); + + /* Add the item in the listbox */ + iItem = (INT)SendMessageW(Globals.hListDlg, LB_ADDSTRING, 0, (LPARAM)szFormat); + if (iItem != LB_ERR) + SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]); + } + } +} + +/** + * @DlgInitHandler + * + * Function which processes several operations for WM_INITDIALOG. + * + * @param[in] hDlg + * The handle object of the dialog. + * + * @return + * TRUE to inform the system that WM_INITDIALOG has been processed and + * that it should set the keyboard focus to the control. + * + */ +BOOL DlgInitHandler(IN HWND hDlg) +{ + INT PosX, PosY; + RECT rc; + WCHAR szAboutDlg[MAX_BUFFER]; + HMENU hSysMenu; + + /* Save the dialog handle */ + Globals.hMainDlg = hDlg; + + /* Center the dialog on the screen */ + GetWindowRect(hDlg, &rc); + PosX = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2; + PosY = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2; + SetWindowPos(hDlg, 0, PosX, PosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + + /* Load the icon resource */ + Globals.hIcon = LoadImageW(Globals.hInstance, + MAKEINTRESOURCEW(IDI_ICON_UTILMAN), + IMAGE_ICON, + 0, + 0, + LR_DEFAULTSIZE); + + /* Set the icon within the dialog's title bar */ + if (Globals.hIcon) + { + SendMessageW(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)Globals.hIcon); + } + + /* Retrieve the system menu and append the "About" menu item onto it */ + hSysMenu = GetSystemMenu(hDlg, FALSE); + if (hSysMenu != NULL) + { + if (LoadStringW(Globals.hInstance, IDM_ABOUT, szAboutDlg, _countof(szAboutDlg))) + { + AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL); + AppendMenuW(hSysMenu, MF_STRING, IDM_ABOUT, szAboutDlg); + } + } + + /* Get the dialog items, specifically the dialog list box, the Start and Stop buttons */ + Globals.hListDlg = GetDlgItem(hDlg, IDC_LISTBOX); + Globals.hDlgCtlStart = GetDlgItem(hDlg, IDC_START); + Globals.hDlgCtlStop = GetDlgItem(hDlg, IDC_STOP); + + /* Initialize the GUI listbox */ + InitUtilsList(TRUE); + + /* Set the selection to the first item */ + Globals.iSelectedIndex = 0; + + /* Refresh the list */ + ListBoxRefreshContents(); + + /* Create a timer, we'll use it to control the state of our items in the listbox */ + Globals.iTimer = SetTimer(hDlg, 0, 400, NULL); + + return TRUE; +} + +/** + * @GroupBoxUpdateTitle + * + * Updates the title of the groupbox. + * + * @return + * Nothing. + * + */ +VOID GroupBoxUpdateTitle(VOID) +{ + WCHAR szFormat[MAX_BUFFER]; + + /* Format the string with the utility's name and set it to the listbox's title */ + StringCchPrintfW(szFormat, _countof(szFormat), Globals.szGrpBoxTitle, EntriesList[Globals.iSelectedIndex].szResource); + SetWindowTextW(GetDlgItem(Globals.hMainDlg, IDC_GROUPBOX), szFormat); +} + +/** + * @UpdateUtilityState + * + * Checks the state of the given accessibility tool. + * + * @param[in] bUtilState + * State condition (boolean TRUE: started / FALSE: stopped). + * + * @return + * Nothing. + * + */ +VOID UpdateUtilityState(IN BOOL bUtilState) +{ + Button_Enable(Globals.hDlgCtlStart, !bUtilState); + Button_Enable(Globals.hDlgCtlStop, bUtilState); + + /* Update the groupbox's title based on the selected utility item */ + GroupBoxUpdateTitle(); +} + +/** + * @ListBoxRefreshContents + * + * Handle the tasks on a periodic cycle. This function handles WM_TIMER message. + * + * @return + * Returns 0 to inform the system that WM_TIMER has been processed. + * + */ +INT ListBoxRefreshContents(VOID) +{ + UINT i; + INT iItem; + BOOL bIsRunning; + WCHAR szFormat[MAX_BUFFER]; + + /* Disable listbox redraw */ + SendMessageW(Globals.hListDlg, WM_SETREDRAW, FALSE, 0); + + for (i = 0; i < _countof(EntriesList); ++i) + { + /* Check the utility's state */ + bIsRunning = IsProcessRunning(EntriesList[i].lpProgram); + if (bIsRunning != EntriesList[i].bState) + { + /* The utility's state has changed, save it */ + EntriesList[i].bState = bIsRunning; + + /* Update the corresponding item in the listbox */ + StringCchPrintfW(szFormat, _countof(szFormat), + (bIsRunning ? Globals.szRunning : Globals.szNotRunning), + EntriesList[i].szResource); + + SendMessageW(Globals.hListDlg, LB_DELETESTRING, (LPARAM)i, 0); + iItem = SendMessageW(Globals.hListDlg, LB_INSERTSTRING, (LPARAM)i, (LPARAM)szFormat); + if (iItem != LB_ERR) + SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]); + } + } + + /* Re-enable listbox redraw */ + SendMessageW(Globals.hListDlg, WM_SETREDRAW, TRUE, 0); + + /* + * Check the previously selected item. This will help us determine what + * item has been selected and set its focus selection back. Furthermore, check + * the state of each accessibility tool and enable/disable the buttons. + */ + SendMessageW(Globals.hListDlg, LB_SETCURSEL, (WPARAM)Globals.iSelectedIndex, 0); + UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState); + + return 0; +} + +/** + * @DlgProc + * + * Main dialog application procedure function. + * + * @param[in] hDlg + * The handle object of the dialog. + * + * @param[in] Msg + * Message events (in unsigned int). + * + * @param[in] wParam + * Message parameter (in UINT_PTR). + * + * @param[in] lParam + * Message paramater (in LONG_PTR). + * + * @return + * Returns 0 to inform the system that the procedure has been handled. + * + */ +INT_PTR APIENTRY DlgProc( + IN HWND hDlg, + IN UINT Msg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + switch (Msg) + { + case WM_INITDIALOG: + DlgInitHandler(hDlg); + return TRUE; + + case WM_CLOSE: + KillTimer(hDlg, Globals.iTimer); + DestroyIcon(Globals.hIcon); + EndDialog(hDlg, FALSE); + break; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_OK: + case IDC_CANCEL: + EndDialog(hDlg, FALSE); + break; + + case IDC_LISTBOX: + { + switch (HIWORD(wParam)) + { + case LBN_SELCHANGE: + { + /* Retrieve the index of the current selected item */ + INT iIndex = SendMessageW(Globals.hListDlg, LB_GETCURSEL, 0, 0); + if ((iIndex == LB_ERR) || (iIndex >= _countof(EntriesList))) + break; + + /* Assign the selected index and check the utility's state */ + Globals.iSelectedIndex = iIndex; + UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState); + break; + } + break; + } + break; + } + + case IDC_START: + LaunchProcess(EntriesList[Globals.iSelectedIndex].lpProgram); + break; + + case IDC_STOP: + CloseProcess(EntriesList[Globals.iSelectedIndex].lpProgram); + break; + + default: + break; + } + break; + } + + case WM_TIMER: + ListBoxRefreshContents(); + return 0; + + case WM_SYSCOMMAND: + { + switch (LOWORD(wParam)) + { + case IDM_ABOUT: + ShowAboutDlg(hDlg); + break; + } + break; + } + } + + return 0; +} + +/** + * @wWinMain + * + * Application entry point. + * + * @param[in] hInstance + * Application instance. + * + * @param[in] hPrevInstance + * The previous instance of the application (not used). + * + * @param[in] pCmdLine + * Pointer to a command line argument (in wide string -- not used). + * + * @param[in] nCmdShow + * An integer served as a flag to note how the application will be shown (not used). + * + * @return + * Returns 0 to let the function terminating before it enters in the message loop. + * + */ +INT WINAPI wWinMain( + IN HINSTANCE hInstance, + IN HINSTANCE hPrevInstance, + IN LPWSTR pCmdLine, + IN INT nCmdShow) +{ + HANDLE hMutex; + DWORD dwError; + INITCOMMONCONTROLSEX iccex; + + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(pCmdLine); + UNREFERENCED_PARAMETER(nCmdShow); + + /* Create a mutant object for the program. */ + hMutex = CreateMutexW(NULL, FALSE, L"Utilman"); + if (hMutex) + { + /* Check if there's already a mutex for the program */ + dwError = GetLastError(); + if (dwError == ERROR_ALREADY_EXISTS) + { + /* + The program's instance is already here. That means + the program is running and we should not set a new instance + and mutex object. + */ + CloseHandle(hMutex); + return 0; + } + } + + /* Load the common controls for the program */ + iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); + iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&iccex); + + /* Initialize the globals */ + ZeroMemory(&Globals, sizeof(Globals)); + Globals.hInstance = hInstance; + + LoadStringW(Globals.hInstance, IDS_RUNNING, + Globals.szRunning, _countof(Globals.szRunning)); + LoadStringW(Globals.hInstance, IDS_NOTRUNNING, + Globals.szNotRunning, _countof(Globals.szNotRunning)); + LoadStringW(Globals.hInstance, IDS_GROUPBOX_OPTIONS_TITLE, + Globals.szGrpBoxTitle, _countof(Globals.szGrpBoxTitle)); + + /* Initialize the list of accessibility utilities */ + InitUtilsList(FALSE); + + /* Create the dialog box of the program */ + DialogBoxW(hInstance, + MAKEINTRESOURCEW(IDD_MAIN_DIALOG), + GetDesktopWindow(), + DlgProc); + + /* Delete the mutex */ + if (hMutex) + { + CloseHandle(hMutex); + } + + return 0; +} diff --git a/base/applications/utilman/lang/en-US.rc b/base/applications/utilman/lang/en-US.rc new file mode 100644 index 00000000000..582c69b57c3 --- /dev/null +++ b/base/applications/utilman/lang/en-US.rc @@ -0,0 +1,54 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: English (United States of America) translation resource + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +IDD_MAIN_DIALOG DIALOGEX 0, 0, 284, 183 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTEXTHELP +CAPTION "Accessibility Utility Manager" +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +FONT 8, "MS Shell Dlg" +BEGIN + LISTBOX IDC_LISTBOX, 4, 4, 273, 56, LBS_STANDARD | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER + CONTROL "", IDC_GROUPBOX, "Button", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 3, 62, 275, 92 + CONTROL "Start", IDC_START, "Button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 76, 45, 16 + CONTROL "Stop", IDC_STOP, "Button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 69, 76, 45, 16 + CONTROL "Start automatically when I log in", IDC_START_LOG_IN, "Button", BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 12, 101, 206, 14 + CONTROL "Start automatically when I look my desktop", IDC_START_DESKTOP, "Button", BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 12, 118, 212, 14 + CONTROL "Start automatically when Utility Manager starts", IDC_START_UTILMAN, "Button", BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 12, 134, 212, 13 + CONTROL "&OK", IDC_OK, "Button", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 160, 161, 50, 14 + CONTROL "&Cancel", IDC_CANCEL, "Button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 221, 161, 50, 14 + CONTROL "&Help", IDC_HELP_TOPICS, "Button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 98, 161, 50, 14 +END + +IDD_ABOUT_DIALOG DIALOGEX 22, 16, 210, 65 +CAPTION "About Accessibility Utility Manager" +FONT 8, "MS Shell Dlg", 0, 0 +STYLE DS_SHELLFONT | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_MODALFRAME +BEGIN + ICON IDI_ICON_UTILMAN, IDC_STATIC, 10, 10, 7, 30 + LTEXT "Accessibility Utility Manager\nCopyright (C) 2019 George Bișoc (fraizeraust99 at gmail dot com)", IDC_STATIC, 48, 7, 150, 36 + LTEXT "Copyright (C) 2019 Hermes Belusca-Maito", IDC_STATIC, 48, 33, 150, 36 + PUSHBUTTON "Close", IDOK, 75, 47, 44, 15 +END + +STRINGTABLE +BEGIN + IDS_OSK "On-Screen Keyboard" + IDS_MAGNIFIER "Magnifier" +END + +STRINGTABLE +BEGIN + IDS_NOTRUNNING "%s is not running" + IDS_RUNNING "%s is running" + IDS_GROUPBOX_OPTIONS_TITLE "Options for %s" +END + +STRINGTABLE +BEGIN + IDM_ABOUT "About Accessibility Utility Manager..." +END diff --git a/base/applications/utilman/precomp.h b/base/applications/utilman/precomp.h new file mode 100644 index 00000000000..db665e793c2 --- /dev/null +++ b/base/applications/utilman/precomp.h @@ -0,0 +1,75 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Pre-compiled header file + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +#ifndef _UTILMAN_H +#define _UTILMAN_H + +/* INCLUDES ******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resource.h" + +/* DEFINES ********************************************************************/ + +#define MAX_BUFFER 256 + +/* TYPES **********************************************************************/ + +typedef struct +{ + HINSTANCE hInstance; + HICON hIcon; + UINT_PTR iTimer; + INT iSelectedIndex; + HWND hDlgCtlStart; + HWND hDlgCtlStop; + HWND hListDlg; + HWND hMainDlg; + WCHAR szRunning[MAX_BUFFER]; + WCHAR szNotRunning[MAX_BUFFER]; + WCHAR szGrpBoxTitle[MAX_BUFFER]; +} UTILMAN_GLOBALS; + +typedef struct _UTILMAN_STATE +{ + LPCWSTR lpProgram; + UINT uNameId; + WCHAR szResource[MAX_BUFFER]; + BOOL bState; +} UTILMAN_STATE, *PUTILMAN_STATE; + +/* DECLARATIONS ***************************************************************/ + +/* dialog.c */ +BOOL DlgInitHandler(HWND hDlg); +INT_PTR APIENTRY DlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam); +INT ListBoxRefreshContents(VOID); +VOID CheckUtilityState(BOOL bUtilState); + +/* process.c */ +BOOL IsProcessRunning(IN LPCWSTR lpProcessName); +BOOL LaunchProcess(LPCWSTR lpProcessName); +BOOL CloseProcess(IN LPCWSTR lpProcessName); + +/* about.c */ +VOID ShowAboutDlg(HWND hDlgParent); +INT_PTR CALLBACK AboutDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam); + +/* Struct variable declaration */ +extern UTILMAN_GLOBALS Globals; + +#endif /* _UTILMAN_H */ + +/* EOF */ diff --git a/base/applications/utilman/process.c b/base/applications/utilman/process.c new file mode 100644 index 00000000000..45bae49276f --- /dev/null +++ b/base/applications/utilman/process.c @@ -0,0 +1,189 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Process handling functions + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +/* INCLUDES *******************************************************************/ + +#include "precomp.h" + +/* FUNCTIONS ******************************************************************/ + +/** + * @IsProcessRunning + * + * Checks if a process is running. + * + * @param[in] ProcName + * The name of the executable process. + * + * @return + * Returns TRUE if the given process' name is running, + * FALSE otherwise. + * + */ +BOOL IsProcessRunning(IN LPCWSTR lpProcessName) +{ + BOOL bIsRunning = FALSE; + PROCESSENTRY32W Process = {0}; + + /* Create a snapshot and check whether the given process' executable name is running */ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (hSnapshot == INVALID_HANDLE_VALUE) + return FALSE; + + Process.dwSize = sizeof(Process); + + /* Enumerate the processes */ + if (Process32FirstW(hSnapshot, &Process)) + { + do + { + if (_wcsicmp(Process.szExeFile, lpProcessName) == 0) + { + /* The process we are searching for is running */ + bIsRunning = TRUE; + break; + } + } + while (Process32NextW(hSnapshot, &Process)); + } + + /* Free the handle and return */ + CloseHandle(hSnapshot); + return bIsRunning; +} + +/** + * @LaunchProcess + * + * Executes a process. + * + * @param[in] lpProcessName + * The name of the executable process. + * + * @return + * Returns TRUE if the process has been launched successfully, + * FALSE otherwise. + * + */ +BOOL LaunchProcess(LPCWSTR lpProcessName) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE hUserToken, hProcessToken; + BOOL bSuccess; + WCHAR ExpandedCmdLine[MAX_PATH]; + + /* Expand the process path string */ + ExpandEnvironmentStringsW(lpProcessName, ExpandedCmdLine, ARRAYSIZE(ExpandedCmdLine)); + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + + /* Get the token of the parent (current) process of the application */ + bSuccess = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &hUserToken); + if (!bSuccess) + { + DPRINT("OpenProcessToken() failed with error -> %lu\n", GetLastError()); + return FALSE; + } + + /* Duplicate a new token so that we can use it to create our process */ + bSuccess = DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hProcessToken); + if (!bSuccess) + { + DPRINT("DuplicateTokenEx() failed with error -> %lu\n", GetLastError()); + CloseHandle(hUserToken); + return FALSE; + } + + /* Finally create the process */ + bSuccess = CreateProcessAsUserW(hProcessToken, + NULL, + ExpandedCmdLine, + NULL, + NULL, + FALSE, + 0, // DETACHED_PROCESS, NORMAL_PRIORITY_CLASS + NULL, + NULL, + &si, + &pi); + + if (!bSuccess) + { + DPRINT("CreateProcessAsUserW() failed with error -> %lu\n", GetLastError()); + CloseHandle(hUserToken); + CloseHandle(hProcessToken); + return FALSE; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(hUserToken); + CloseHandle(hProcessToken); + return TRUE; +} + +/** + * @CloseProcess + * + * Closes a process. + * + * @param[in] lpProcessName + * The name of the executable process. + * + * @return + * Returns TRUE if the process has been terminated successfully, + * FALSE otherwise. + * + */ +BOOL CloseProcess(IN LPCWSTR lpProcessName) +{ + BOOL bSuccess = FALSE; + PROCESSENTRY32W Process = {0}; + + /* Create a snapshot and check if the given process' executable name is running */ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (hSnapshot == INVALID_HANDLE_VALUE) + return FALSE; + + Process.dwSize = sizeof(Process); + + /* Enumerate the processes */ + if (Process32FirstW(hSnapshot, &Process)) + { + do + { + if (_wcsicmp(Process.szExeFile, lpProcessName) == 0) + { + /* + * We have found the process. However we must make + * sure that we DO NOT kill ourselves (the process ID + * matching with the current parent process ID). + */ + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, Process.th32ProcessID); + if ((hProcess != NULL) && (Process.th32ProcessID != GetCurrentProcessId())) + { + TerminateProcess(hProcess, 0); + CloseHandle(hProcess); + } + bSuccess = TRUE; + break; + } + } + while (Process32NextW(hSnapshot, &Process)); + } + + /* Free the handle and return */ + CloseHandle(hSnapshot); + return bSuccess; +} diff --git a/base/applications/utilman/res/utilman.ico b/base/applications/utilman/res/utilman.ico new file mode 100644 index 00000000000..9f37366fb10 Binary files /dev/null and b/base/applications/utilman/res/utilman.ico differ diff --git a/base/applications/utilman/resource.h b/base/applications/utilman/resource.h new file mode 100644 index 00000000000..df93ab48ad9 --- /dev/null +++ b/base/applications/utilman/resource.h @@ -0,0 +1,47 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Resource header file + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +#pragma once + +#define IDC_STATIC -1 + +/* Icon resource */ +#define IDI_ICON_UTILMAN 50 + +/* List box resource */ +#define IDC_LISTBOX 60 + +/* Utility tools name strings */ +#define IDS_OSK 100 +#define IDS_MAGNIFIER 101 + +/* Utility state strings */ +#define IDS_NOTRUNNING 150 +#define IDS_RUNNING 151 + +/* Groupbox option title */ +#define IDC_GROUPBOX 200 +#define IDS_GROUPBOX_OPTIONS_TITLE 201 + +/* Button resources */ +#define IDC_START 300 +#define IDC_STOP 301 +#define IDC_OK 302 +#define IDC_CANCEL 303 +#define IDC_HELP_TOPICS 304 + +/* Checkbox resources */ +#define IDC_START_LOG_IN 400 +#define IDC_START_DESKTOP 401 +#define IDC_START_UTILMAN 402 + +/* System menu (About) resource */ +#define IDM_ABOUT 600 + +/* Main dialog resource */ +#define IDD_MAIN_DIALOG 1000 +#define IDD_ABOUT_DIALOG 1050 diff --git a/base/applications/utilman/utilman.rc b/base/applications/utilman/utilman.rc new file mode 100644 index 00000000000..50bfa4d710c --- /dev/null +++ b/base/applications/utilman/utilman.rc @@ -0,0 +1,32 @@ +/* + * PROJECT: ReactOS Utility Manager (Accessibility) + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Main resource file + * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com) + */ + +/* INCLUDES ******************************************************************/ + +#include +#include +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Accessibility Utility Manager" +#define REACTOS_STR_INTERNAL_NAME "utilman" +#define REACTOS_STR_ORIGINAL_FILENAME "utilman.exe" +#include + +IDI_ICON_UTILMAN ICON "res/utilman.ico" + +#include + +/* UTF-8 */ +#pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif + +/* EOF */ diff --git a/media/inf/shortcuts.inf b/media/inf/shortcuts.inf index f6e3a4c0977..cc59005e55d 100644 --- a/media/inf/shortcuts.inf +++ b/media/inf/shortcuts.inf @@ -50,6 +50,7 @@ QuickLaunchShortcuts=26, Microsoft\Internet Explorer\Quick Launch [AccessibilityShortcuts] %SystemRoot%\system32\magnify.exe, %MAGNIFY_TITLE%, %MAGNIFY_DESC%, 0, %HOMEDRIVE%%HOMEPATH% %SystemRoot%\system32\osk.exe, %OSK_TITLE%, %OSK_DESC%, 0, %HOMEDRIVE%%HOMEPATH% +%SystemRoot%\system32\utilman.exe, %UTILMAN_TITLE%, %UTILMAN_DESC%, 0, %HOMEDRIVE%%HOMEPATH% [EntertainmentShortcuts] %SystemRoot%\system32\mplay32.exe, %MPLAY_TITLE%, %MPLAY_DESC%, 0, %HOMEDRIVE%%HOMEPATH% @@ -133,6 +134,8 @@ WINMINE_TITLE=WineMine WINMINE_DESC=WineMine SPIDER_TITLE=Spider Solitaire SPIDER_DESC=Spider Solitaire +UTILMAN_TITLE=Accessibility Utility Manager +UTILMAN_DESC=Launch Accessibility Utility Manager ; Bulgarian [Strings.0402]