mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 14:18:41 +00:00
![Kyle Katarn](/assets/img/avatar_default.png)
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.
427 lines
12 KiB
C
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);
|
|
}
|