Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

985 lines
31 KiB

/**********************************************************************/
/** Microsoft Windows/NT **/
/** Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/
/*
service.cpp
Calls to start and stop services.
FILE HISTORY:
*/
#include "stdafx.h"
#include "DynamLnk.h"
#include "cluster.h"
DynamicDLL g_NetApiDLL( _T("NETAPI32.DLL"), g_apchNetApiFunctionNames );
/*---------------------------------------------------------------------------
IsComputerNT
Checks to see if the given computer is running NT
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSIsComputerNT
(
LPCTSTR pszComputer,
BOOL * bIsNT
)
{
DWORD err = 0;
BYTE * pbBuffer;
*bIsNT = FALSE;
if ( !g_NetApiDLL.LoadFunctionPointers() )
return err;
err = ((NETSERVERGETINFO) g_NetApiDLL[NET_API_NET_SERVER_GET_INFO])
( (LPTSTR) pszComputer,
101,
&pbBuffer );
if (err == NERR_Success)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_LEVEL
// ERROR_INVALID_PARAMETER
// ERROR_NOT_ENOUGH_MEMORY
//
SERVER_INFO_101 * pServerInfo = (SERVER_INFO_101 *) pbBuffer;
if ( (pServerInfo->sv101_type & SV_TYPE_NT) )
{
*bIsNT = TRUE;
}
err = ERROR_SUCCESS; //Translate the NERR code to a winerror code
}
return err;
}
/*---------------------------------------------------------------------------
IsNTServer
Checks to see if the given computer is running NTS
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSIsNTServer
(
LPCTSTR pszComputer,
BOOL * bIsNTS
)
{
DWORD err = 0;
BYTE * pbBuffer;
*bIsNTS = FALSE;
if ( !g_NetApiDLL.LoadFunctionPointers() )
return err;
err = ((NETSERVERGETINFO) g_NetApiDLL[NET_API_NET_SERVER_GET_INFO])
( (LPTSTR) pszComputer,
101,
&pbBuffer );
if (err == NERR_Success)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_LEVEL
// ERROR_INVALID_PARAMETER
// ERROR_NOT_ENOUGH_MEMORY
//
SERVER_INFO_101 * pServerInfo = (SERVER_INFO_101 *) pbBuffer;
if ( (pServerInfo->sv101_type & SV_TYPE_SERVER_NT) ||
(pServerInfo->sv101_type & SV_TYPE_DOMAIN_CTRL) ||
(pServerInfo->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) )
{
*bIsNTS = TRUE;
}
err = ERROR_SUCCESS; //Translate the NERR code to a winerror code
}
return err;
}
/*---------------------------------------------------------------------------
TFSIsServiceRunning
Checks to see if the given service is running on a machine
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSIsServiceRunning
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
BOOL * fIsRunning
)
{
DWORD err = 0;
DWORD dwStatus;
*fIsRunning = FALSE;
err = TFSGetServiceStatus(pszComputer, pszServiceName, &dwStatus, NULL);
if (err == 0)
*fIsRunning = (BOOL)(dwStatus & SERVICE_RUNNING);
return err;
}
/*!--------------------------------------------------------------------------
TFSGetServiceStatus
Returns ERROR_SUCCESS on API success.
Returns an error code otherwise.
pszComputer - name of the computer to attach to.
pszServiceName - name of the service to check.
pdwServiceStatus - returns the status of the service.
pdwErrorCode - returns the error code returned from the service
(this is NOT the error code from the API itself).
This may be NULL.
Author: KennT
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSGetServiceStatus
(
LPCWSTR pszComputer,
LPCWSTR pszServiceName,
DWORD * pdwServiceStatus,
OPTIONAL DWORD * pdwErrorCode
)
{
DWORD err = 0;
SC_HANDLE hScManager;
Assert(pdwServiceStatus);
*pdwServiceStatus = 0;
if (pdwErrorCode)
*pdwErrorCode = 0;
//
// Find out if the service is running on the given machine
//
hScManager = ::OpenSCManager(pszComputer, NULL, GENERIC_READ);
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(hScManager, pszServiceName, SERVICE_QUERY_STATUS);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
SERVICE_STATUS serviceStatus;
if (!::QueryServiceStatus(hService, &serviceStatus))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
*pdwServiceStatus = serviceStatus.dwCurrentState;
// Also return the error code
if (pdwErrorCode)
{
if (serviceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR)
*pdwErrorCode = serviceStatus.dwServiceSpecificExitCode;
else
*pdwErrorCode = serviceStatus.dwWin32ExitCode;
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
/*---------------------------------------------------------------------------
StartService
Starts the given service on a machine
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSStartService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD err = 0;
err = StartSCMService(pszComputer, pszServiceName, pszServiceDesc);
return err;
}
/*---------------------------------------------------------------------------
StartServiceEx
Starts the given service on a machine, cluster aware
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSStartServiceEx
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszClusterResourceType,
LPCTSTR pszServiceDesc
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD err = 0;
if (FIsComputerInRunningCluster(pszComputer))
{
err = ControlClusterService(pszComputer, pszClusterResourceType, pszServiceDesc, TRUE);
}
else
{
err = StartSCMService(pszComputer, pszServiceName, pszServiceDesc);
}
return err;
}
/*---------------------------------------------------------------------------
StopService
Stops the given service on a machine
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSStopService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD err = 0;
err = StopSCMService(pszComputer, pszServiceName, pszServiceDesc);
return err;
}
/*---------------------------------------------------------------------------
StopServiceEx
Stops the given service on a machine, cluster aware
Author: EricDav
---------------------------------------------------------------------------*/
TFSCORE_API(DWORD)
TFSStopServiceEx
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszClusterResourceType,
LPCTSTR pszServiceDesc
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD err = 0;
if (FIsComputerInRunningCluster(pszComputer))
{
err = ControlClusterService(pszComputer, pszClusterResourceType, pszServiceDesc, FALSE);
}
else
{
err = StopSCMService(pszComputer, pszServiceName, pszServiceDesc);
}
return err;
}
TFSCORE_API(DWORD) TFSGetServiceStartType(LPCWSTR pszComputer, LPCWSTR pszServiceName, DWORD *pdwStartType)
{
DWORD err = 0;
SC_HANDLE hScManager = 0;
SC_HANDLE hService = 0;
HRESULT hr = hrOK;
BOOL fReturn = FALSE;
LPQUERY_SERVICE_CONFIG pqsConfig = NULL;
DWORD cbNeeded = sizeof( QUERY_SERVICE_CONFIG );
DWORD cbSize;
//
// Find out if the service is running on the given machine
//
hScManager = ::OpenSCManager(pszComputer, NULL, GENERIC_READ);
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
err = GetLastError();
goto Exit;
}
hService = ::OpenService(hScManager, pszServiceName, SERVICE_QUERY_CONFIG);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
goto Exit;
}
COM_PROTECT_TRY
{
*pdwStartType = 0;
// loop, allocating the needed size
do
{
delete [] (PBYTE)pqsConfig;
pqsConfig = (LPQUERY_SERVICE_CONFIG) new BYTE[cbNeeded];
cbSize = cbNeeded;
fReturn = ::QueryServiceConfig( hService,
pqsConfig,
cbSize,
&cbNeeded );
*pdwStartType = pqsConfig->dwStartType;
delete [] (PBYTE)pqsConfig;
pqsConfig = NULL;
if (!fReturn && (cbNeeded == cbSize))
{
// error
*pdwStartType = 0;
err = GetLastError();
goto Error;
}
} while (!fReturn && (cbNeeded != cbSize));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH;
if (!FHrSucceeded(hr))
{
// The only time we should get here (with an hr is for outofmemory)
err = ERROR_OUTOFMEMORY;
}
Exit:
if (err != 0)
{
*pdwStartType = 0;
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
TFSCORE_API(DWORD) TFSSetServiceStartType(LPCWSTR pszComputer, LPCWSTR pszServiceName, DWORD dwStartType)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD err = 0;
SC_HANDLE hScManager;
//
// Open the SCManager so that we can try to stop the service
//
hScManager = ::OpenSCManager(pszComputer, NULL, SC_MANAGER_ALL_ACCESS);
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(hScManager, pszServiceName, SERVICE_STOP | SERVICE_ALL_ACCESS);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
if (!::ChangeServiceConfig( hService,
SERVICE_NO_CHANGE,
dwStartType,
SERVICE_NO_CHANGE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_CIRCULAR_DEPENDENCY
// ERROR_DUP_NAME
// ERROR_INVALID_HANDLE
// ERROR_INVALID_PARAMETER
// ERROR_INVALID_SERVICE_ACCOUNT
// ERROR_SERVICE_MARKED_FOR_DELETE
//
err = ::GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
DWORD
StartSCMService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
DWORD err = 0;
SC_HANDLE hScManager;
//
// Open the SCManager so that we can try to start the service
//
hScManager = ::OpenSCManager(pszComputer, NULL, SC_MANAGER_CONNECT );
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(hScManager, pszServiceName, SERVICE_START | SERVICE_QUERY_STATUS);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
SERVICE_STATUS serviceStatus;
if (!::QueryServiceStatus(hService, &serviceStatus))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
// If the service is in a start pending, do not do anything
if (serviceStatus.dwCurrentState == SERVICE_START_PENDING)
{
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
err = ERROR_SERVICE_ALREADY_RUNNING;
return err;
}
if (!::StartService(hService, NULL, NULL))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_PATH_NOT_FOUND
// ERROR_SERVICE_ALREADY_RUNNING
// ERROR_SERVICE_DATABASE_LOCKED
// ERROR_SERVICE_DEPENDENCY_DELETED
// ERROR_SERVICE_DEPENDENCY_FAIL
// ERROR_SERVICE_DISABLED
// ERROR_SERVICE_LOGON_FAILED
// ERROR_SERVICE_MARKED_FOR_DELETE
// ERROR_SERVICE_NO_THREAD
// ERROR_SERVICE_REQUEST_TIMEOUT
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
//
// Put up the dialog with the funky spinning thing to
// let the user know that something is happening
//
CServiceCtrlDlg dlgServiceCtrl(hService, pszComputer, pszServiceDesc, TRUE);
dlgServiceCtrl.DoModal();
err = dlgServiceCtrl.m_dwErr;
//
// Everything started ok, close up and get going
//
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
DWORD
StopSCMService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
DWORD err = 0;
SC_HANDLE hScManager;
LPENUM_SERVICE_STATUS lpScStatus = NULL;
DWORD dwNumService = 0, dwSize = 0, dwSizeReqd = 0, i = 0;
BOOL bRet;
//
// Open the SCManager so that we can try to stop the service
//
hScManager = ::OpenSCManager(pszComputer, NULL, SC_MANAGER_CONNECT );
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(
hScManager, pszServiceName,
SERVICE_STOP | SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS
);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
//
// Stop all dependent services that are currently active.
// Since the number of services is unknown (up front),
// EnumDependentServices is called atleast twice (first
// to get the size of buffer, and second to get the
// list of services). If the number of services changes
// between the calls, then EnumDependentServices is called
// one more time (as per the logic, i < 3).
//
do
{
//
// Enumerate all dependent services
//
bRet = ::EnumDependentServices(
hService, SERVICE_ACTIVE, lpScStatus, dwSize,
&dwSizeReqd, &dwNumService
);
if (!bRet && (GetLastError() == ERROR_MORE_DATA))
{
//
// Not enough buffer to hold the dependent service list,
// delete previous allocation (if any) and allocate new
// buffer of requiste size.
//
if (lpScStatus) { delete lpScStatus; lpScStatus = NULL; }
lpScStatus = reinterpret_cast<LPENUM_SERVICE_STATUS>
(new BYTE[2 * dwSizeReqd]);
if (lpScStatus == NULL)
{
//
// allocation failed, forget about stopping dependent
// services
//
break;
}
//
// Increment attempt count. At most 3 attempts will be made
// to get the list of dependent services
//
dwSize = 2 * dwSizeReqd;
dwSizeReqd = 0;
i++;
}
else
{
//
// Success or failure for other than insufficent buffer reason
//
break;
}
} while( i < 3 );
//
// if dependent service were successfully enumerated
// stop them all
//
if (bRet)
{
for (i = 0; i < dwNumService; i++)
{
StopSCMService(
pszComputer,
lpScStatus[i].lpServiceName,
lpScStatus[i].lpDisplayName
);
}
}
if (lpScStatus) { delete lpScStatus; lpScStatus = NULL; }
//
// Stop the service, now that all dependents have been stopped
//
SERVICE_STATUS serviceStatus;
if (!::ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DEPENDENT_SERVICES_RUNNING
// ERROR_INVALID_SERVICE_CONTROL
// ERROR_SERVICE_CANNOT_ACCEPT_CTRL
// ERROR_SERVICE_NOT_ACTIVE
// ERROR_SERVICE_REQUEST_TIMEOUT
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
if ( serviceStatus.dwCurrentState != SERVICE_STOPPED )
{
//
// Put up the dialog with the funky spinning thing to
// let the user know that something is happening
//
CServiceCtrlDlg dlgServiceCtrl(hService, pszComputer, pszServiceDesc, FALSE);
dlgServiceCtrl.DoModal();
err = dlgServiceCtrl.m_dwErr;
}
//
// Everything stopped ok, close up and get going
//
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
TFSCORE_API(DWORD) TFSPauseService(LPCTSTR pszComputer, LPCTSTR pszServiceName, LPCTSTR pszServiceDesc)
{
return PauseSCMService(pszComputer, pszServiceName, pszServiceDesc);
}
TFSCORE_API(DWORD) TFSResumeService(LPCTSTR pszComputer, LPCTSTR pszServiceName, LPCTSTR pszServiceDesc)
{
return ResumeSCMService(pszComputer, pszServiceName, pszServiceDesc);
}
DWORD
PauseSCMService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
DWORD err = 0;
SC_HANDLE hScManager;
//
// Open the SCManager so that we can try to stop the service
//
hScManager = ::OpenSCManager(pszComputer, NULL, SC_MANAGER_CONNECT );
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(hScManager, pszServiceName, SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
SERVICE_STATUS serviceStatus;
if (!::ControlService(hService, SERVICE_CONTROL_PAUSE, &serviceStatus))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DEPENDENT_SERVICES_RUNNING
// ERROR_INVALID_SERVICE_CONTROL
// ERROR_SERVICE_CANNOT_ACCEPT_CTRL
// ERROR_SERVICE_NOT_ACTIVE
// ERROR_SERVICE_REQUEST_TIMEOUT
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
#if 0
if ( serviceStatus.dwCurrentState != SERVICE_STOPPED )
{
//
// Put up the dialog with the funky spinning thing to
// let the user know that something is happening
//
CServiceCtrlDlg dlgServiceCtrl(hService, pszComputer, pszServiceDesc, FALSE);
dlgServiceCtrl.DoModal();
err = dlgServiceCtrl.m_dwErr;
}
#endif
//
// Everything stopped ok, close up and get going
//
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
DWORD
ResumeSCMService
(
LPCTSTR pszComputer,
LPCTSTR pszServiceName,
LPCTSTR pszServiceDesc
)
{
DWORD err = 0;
SC_HANDLE hScManager;
//
// Open the SCManager so that we can try to stop the service
//
hScManager = ::OpenSCManager(pszComputer, NULL, SC_MANAGER_CONNECT );
if (hScManager == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DATABASE_DOES_NOT_EXIST
// ERROR_INVALID_PARAMETER
//
return GetLastError();
}
SC_HANDLE hService = ::OpenService(hScManager, pszServiceName, SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS);
if (hService == NULL)
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_INVALID_HANDLE
// ERROR_INVALID_NAME
// ERROR_SERVICE_DOES_NOT_EXIST
//
err = GetLastError();
::CloseServiceHandle(hScManager);
return err;
}
SERVICE_STATUS serviceStatus;
if (!::ControlService(hService, SERVICE_CONTROL_CONTINUE, &serviceStatus))
{
//
// Possible Errors:
// ERROR_ACCESS_DENIED
// ERROR_DEPENDENT_SERVICES_RUNNING
// ERROR_INVALID_SERVICE_CONTROL
// ERROR_SERVICE_CANNOT_ACCEPT_CTRL
// ERROR_SERVICE_NOT_ACTIVE
// ERROR_SERVICE_REQUEST_TIMEOUT
//
err = GetLastError();
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}
#if 0
if ( serviceStatus.dwCurrentState != SERVICE_STOPPED )
{
//
// Put up the dialog with the funky spinning thing to
// let the user know that something is happening
//
CServiceCtrlDlg dlgServiceCtrl(hService, pszComputer, pszServiceDesc, FALSE);
dlgServiceCtrl.DoModal();
err = dlgServiceCtrl.m_dwErr;
}
#endif
//
// Everything stopped ok, close up and get going
//
::CloseServiceHandle(hService);
::CloseServiceHandle(hScManager);
return err;
}