reactos/base/applications/mscutils/servman/progress.c
Kyle Katarn b3947d5283
[SERVMAN] UI update and Error Management (#2653)
Purpose
=======
- Current design does not warn user nor logs DEBUG traces when Service Start/Stop command fails or reach timeout.
- Current Service Start/Stop progress window are WS_EX_TOOLWINDOW which reduce lisibility, is a ReactOS specificity without good reason.

Proposed changes
================
- DPRINT1 traces added on failure cases.
- Error Message box presented to user upon failure with explicit root cause identification.
- Change Dialog definition to standard window.
2020-05-01 19:01:59 +02:00

427 lines
12 KiB
C

/*
* PROJECT: ReactOS Services
* LICENSE: GPL - See COPYING in the top level directory
* FILE: base/applications/mscutils/servman/progress.c
* PURPOSE: Progress dialog box message handler
* COPYRIGHT: Copyright 2006-2015 Ged Murphy <gedmurphy@reactos.org>
*
*/
#include "precomp.h"
#include <process.h>
#define PROGRESS_RANGE 20
#define PROGRESS_STEP_MAX 15
typedef struct _PROGRESS_DATA
{
HWND hDlg;
HWND hProgress;
LPWSTR ServiceName;
ULONG Action;
BOOL StopDepends;
LPWSTR ServiceList;
PVOID Param;
} PROGRESS_DATA, *PPROGRESS_DATA;
VOID ShowError(DWORD dwLastError)
{
LPWSTR lpMsg;
if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwLastError,
LANG_USER_DEFAULT,
(LPWSTR)&lpMsg,
0, NULL))
{
return;
}
MessageBoxW(NULL, lpMsg, NULL, MB_OK | MB_ICONERROR);
LocalFree(lpMsg);
}
static VOID
ResetProgressDialog(HWND hDlg,
LPWSTR ServiceName,
ULONG LabelId)
{
LPWSTR lpProgStr;
/* Load the label Id */
if (AllocAndLoadString(&lpProgStr,
hInstance,
LabelId))
{
/* Write it to the dialog */
SendDlgItemMessageW(hDlg,
IDC_SERVCON_INFO,
WM_SETTEXT,
0,
(LPARAM)lpProgStr);
LocalFree(lpProgStr);
}
/* Write the service name to the dialog */
SendDlgItemMessageW(hDlg,
IDC_SERVCON_NAME,
WM_SETTEXT,
0,
(LPARAM)ServiceName);
/* Set the progress bar to the start */
SendDlgItemMessageW(hDlg,
IDC_SERVCON_PROGRESS,
PBM_SETPOS,
0,
0);
}
unsigned int __stdcall ActionThread(void* Param)
{
PPROGRESS_DATA ProgressData = (PPROGRESS_DATA)Param;
DWORD dwResult;
if (ProgressData->Action == ACTION_START)
{
/* Setup the progress dialog for this action */
ResetProgressDialog(ProgressData->hDlg,
ProgressData->ServiceName,
IDS_PROGRESS_INFO_START);
/* Start the service */
dwResult = DoStartService(ProgressData->ServiceName,
ProgressData->hProgress,
ProgressData->Param);
if (dwResult == ERROR_SUCCESS)
{
/* We're done, slide the progress bar up to the top */
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
}
else if (ProgressData->Action == ACTION_STOP || ProgressData->Action == ACTION_RESTART)
{
/* Check if there are and dependants to stop */
if (ProgressData->StopDepends && ProgressData->ServiceList)
{
LPWSTR lpStr = ProgressData->ServiceList;
/* Loop through all the services in the list */
for (;;)
{
/* Break when we hit the double null */
if (*lpStr == L'\0' && *(lpStr + 1) == L'\0')
break;
/* If this isn't our first time in the loop we'll
have been left on a null char */
if (*lpStr == L'\0')
lpStr++;
ResetProgressDialog(ProgressData->hDlg,
lpStr,
IDS_PROGRESS_INFO_STOP);
/* Stop the requested service */
dwResult = DoStopService(ProgressData->ServiceName,
ProgressData->hProgress);
if (dwResult == ERROR_SUCCESS)
{
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
/* Move onto the next string */
while (*lpStr != L'\0')
lpStr++;
}
}
ResetProgressDialog(ProgressData->hDlg,
ProgressData->ServiceName,
IDS_PROGRESS_INFO_STOP);
dwResult = DoStopService(ProgressData->ServiceName,
ProgressData->hProgress);
if (dwResult == ERROR_SUCCESS)
{
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
/* If this was a restart, we'll need to start the service back up */
if (ProgressData->Action == ACTION_RESTART)
{
/* Setup the progress dialog for this action */
ResetProgressDialog(ProgressData->hDlg,
ProgressData->ServiceName,
IDS_PROGRESS_INFO_START);
/* Start the service */
dwResult = DoStartService(ProgressData->ServiceName,
ProgressData->hProgress,
NULL);
if (dwResult == ERROR_SUCCESS)
{
/* We're done, slide the progress bar up to the top */
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
}
}
else if (ProgressData->Action == ACTION_PAUSE)
{
/* Setup the progress dialog for this action */
ResetProgressDialog(ProgressData->hDlg,
ProgressData->ServiceName,
IDS_PROGRESS_INFO_PAUSE);
/* Pause the service */
dwResult = DoControlService(ProgressData->ServiceName,
ProgressData->hProgress,
SERVICE_CONTROL_PAUSE);
if (dwResult == ERROR_SUCCESS)
{
/* We're done, slide the progress bar up to the top */
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
}
else if (ProgressData->Action == ACTION_RESUME)
{
/* Setup the progress dialog for this action */
ResetProgressDialog(ProgressData->hDlg,
ProgressData->ServiceName,
IDS_PROGRESS_INFO_RESUME);
/* resume the service */
dwResult = DoControlService(ProgressData->ServiceName,
ProgressData->hProgress,
SERVICE_CONTROL_CONTINUE);
if (dwResult == ERROR_SUCCESS)
{
/* We're done, slide the progress bar up to the top */
CompleteProgressBar(ProgressData->hProgress);
}
else
{
ShowError(dwResult);
}
}
EndDialog(ProgressData->hDlg, IDOK);
_endthreadex(0);
return 0;
}
static BOOL
InitProgressDialog(HWND hDlg,
UINT Message,
WPARAM wParam,
LPARAM lParam)
{
PPROGRESS_DATA ProgressData = (PPROGRESS_DATA)lParam;
HANDLE hThread;
ProgressData->hDlg = hDlg;
/* Get a handle to the progress bar */
ProgressData->hProgress = GetDlgItem(hDlg,
IDC_SERVCON_PROGRESS);
if (!ProgressData->hProgress)
return FALSE;
/* Set the progress bar range */
SendMessageW(ProgressData->hProgress,
PBM_SETRANGE,
0,
MAKELPARAM(0, PROGRESS_RANGE));
/* Set the progress bar step */
SendMessageW(ProgressData->hProgress,
PBM_SETSTEP,
(WPARAM)1,
0);
/* Create a thread to handle the service control */
hThread = (HANDLE)_beginthreadex(NULL, 0, &ActionThread, ProgressData, 0, NULL);
if (!hThread) return FALSE;
CloseHandle(hThread);
return TRUE;
}
INT_PTR CALLBACK
ProgressDialogProc(HWND hDlg,
UINT Message,
WPARAM wParam,
LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
{
return InitProgressDialog(hDlg, Message, wParam, lParam);
}
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hDlg, wParam);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
VOID
CompleteProgressBar(HANDLE hProgress)
{
HWND hProgBar = (HWND)hProgress;
UINT Pos = 0;
/* Get the current position */
Pos = SendMessageW(hProgBar,
PBM_GETPOS,
0,
0);
/* Loop until we hit the max */
while (Pos <= PROGRESS_RANGE)
{
/* Increment the progress bar */
SendMessageW(hProgBar,
PBM_DELTAPOS,
Pos,
0);
/* Wait for 15ms to give it a smooth feel */
Sleep(15);
Pos++;
}
}
VOID
IncrementProgressBar(HANDLE hProgress,
UINT Step)
{
HWND hProgBar = (HWND)hProgress;
UINT Position;
/* Don't allow the progress to reach to complete*/
Position = SendMessageW(hProgBar,
PBM_GETPOS,
0,
0);
if (Position < PROGRESS_STEP_MAX)
{
/* Do we want to increment the default amount? */
if (Step == DEFAULT_STEP)
{
/* Use the step value we set on create */
SendMessageW(hProgBar,
PBM_STEPIT,
0,
0);
}
else
{
/* Use the value passed */
SendMessageW(hProgBar,
PBM_SETPOS,
Step,
0);
}
}
}
BOOL
RunActionWithProgress(HWND hParent,
LPWSTR ServiceName,
LPWSTR DisplayName,
UINT Action,
PVOID Param)
{
PROGRESS_DATA ProgressData;
LPWSTR ServiceList;
BOOL StopDepends;
INT_PTR Result;
StopDepends = FALSE;
ServiceList = NULL;
/* Check if we'll be stopping the service */
if (Action == ACTION_STOP || Action == ACTION_RESTART)
{
/* Does the service have any dependent services which need stopping first */
ServiceList = GetListOfServicesToStop(ServiceName);
if (ServiceList)
{
/* Ask the user if they want to stop the dependants */
StopDepends = CreateStopDependsDialog(hParent,
ServiceName,
DisplayName,
ServiceList);
/* Exit early if the user decided not to stop the dependants */
if (StopDepends == FALSE)
{
HeapFree(GetProcessHeap(), 0, ServiceList);
return FALSE;
}
}
}
ProgressData.hDlg = NULL;
ProgressData.ServiceName = ServiceName;
ProgressData.Action = Action;
ProgressData.StopDepends = StopDepends;
ProgressData.ServiceList = ServiceList;
ProgressData.Param = Param;
Result = DialogBoxParamW(hInstance,
MAKEINTRESOURCEW(IDD_DLG_PROGRESS),
hParent,
ProgressDialogProc,
(LPARAM)&ProgressData);
if (ServiceList)
HeapFree(GetProcessHeap(), 0, ServiceList);
return (Result == IDOK);
}