|
|
/*++ '
Copyright (c) 1997 Microsoft Corporation
Module Name:
STISvc.CPP
Abstract:
Code for performing STI service related functions ( Start/Stop etc) It is separated from main process code make it possible to share process for multiple services ever needed
Author:
Vlad Sadovsky (vlads) 09-20-97
Environment:
User Mode - Win32
Revision History:
22-Sep-1997 VladS created
--*/
//
// Include Headers
//
#include "precomp.h"
#include "stiexe.h"
#include "device.h"
#include <stisvc.h>
#include <regstr.h>
#include <devguid.h>
typedef LONG NTSTATUS; #include <svcs.h>
extern HWND g_hStiServiceWindow;
extern BOOL StiRefreshWithDelay( ULONG ulDelay, WPARAM wParam, LPARAM lParam);
//
// Delay in milliseconds to wait before processing PnP device event
//
#define DEVICEEVENT_WAIT_TIME 1000
//
// Local variables and types definitions
//
//
// Service status data
//
SERVICE_STATUS g_StiServiceStatus;
//
// Handle of registered service, used for updating running status
//
SERVICE_STATUS_HANDLE g_StiServiceStatusHandle;
//
// Initialization flag
//
BOOL g_fStiServiceInitialized = FALSE;
//
// What type of sink to use
//
#ifdef WINNT
BOOL g_fUseServiceCtrlSink = TRUE; #else
BOOL g_fUseServiceCtrlSink = FALSE; #endif
//
// Hidden service window
//
HWND g_hStiServiceWindow = NULL;
//
// Notification sink for PnP notifications
//
HDEVNOTIFY g_hStiServiceNotificationSink = NULL;
//
// Shutdown event
//
HANDLE hShutdownEvent = NULL;
#ifdef WINNT
//
// Local prototypes
//
BOOL WINAPI InitializeNTSecurity( VOID );
BOOL WINAPI TerminateNTSecurity( VOID ); #endif
//
// Service status variable dispatch table
//
SERVICE_TABLE_ENTRY ServiceDispatchTable[] = { { STI_SERVICE_NAME, StiServiceMain }, { NULL, NULL } };
//
// Code section
//
DWORD WINAPI UpdateServiceStatus( IN DWORD dwState, IN DWORD dwWin32ExitCode, IN DWORD dwWaitHint ) /*++
Description:
Updates the local copy status of service controller status and reports it to the service controller.
Arguments:
dwState - New service state.
dwWin32ExitCode - Service exit code.
dwWaitHint - Wait hint for lengthy state transitions.
Returns:
NO_ERROR on success and returns Win32 error if failure. On success the status is reported to service controller.
--*/ {
const TCHAR* szStateDbgMsg[] = { TEXT("SERVICE_UNKNOWN "), // 0x00000000
TEXT("SERVICE_STOPPED "), // 0x00000001
TEXT("SERVICE_START_PENDING "), // 0x00000002
TEXT("SERVICE_STOP_PENDING "), // 0x00000003
TEXT("SERVICE_RUNNING "), // 0x00000004
TEXT("SERVICE_CONTINUE_PENDING "), // 0x00000005
TEXT("SERVICE_PAUSE_PENDING "), // 0x00000006
TEXT("SERVICE_PAUSED "), // 0x00000007
TEXT("SERVICE_UNKNOWN "), // 0x00000008
};
DWORD dwError = NO_ERROR;
//
// If state is changing - save the new one
//
if (dwState) { g_StiServiceStatus.dwCurrentState = dwState; }
g_StiServiceStatus.dwWin32ExitCode = dwWin32ExitCode; g_StiServiceStatus.dwWaitHint = dwWaitHint;
//
// If we are in the middle of lengthy operation, increment checkpoint value
//
if ((g_StiServiceStatus.dwCurrentState == SERVICE_RUNNING) || (g_StiServiceStatus.dwCurrentState == SERVICE_STOPPED) ) { g_StiServiceStatus.dwCheckPoint = 0; } else { g_StiServiceStatus.dwCheckPoint++; }
#ifdef WINNT
//
// Now update SCM running database
//
if ( g_fRunningAsService ) {
DBG_TRC(("Updating service status. CurrentState=%S StateCode=%d", g_StiServiceStatus.dwCurrentState < (sizeof(szStateDbgMsg) / sizeof(TCHAR *)) ? szStateDbgMsg[g_StiServiceStatus.dwCurrentState] : szStateDbgMsg[0], g_StiServiceStatus.dwCurrentState));
if( !SetServiceStatus( g_StiServiceStatusHandle, &g_StiServiceStatus ) ) { dwError = GetLastError(); } else { dwError = NO_ERROR; } }
#endif
return ( dwError);
} // UpdateServiceStatus()
DWORD WINAPI StiServiceInitialize( VOID ) /*++
Routine Description:
Service initialization, creates all needed data structures
Nb: This routine has upper limit for execution time, so if it takes too much time separate thread will have to be created to queue initialization work
Arguments:
Return Value:
None.
--*/ { HRESULT hres; DWORD dwError;
DBG_FN(StiServiceInitialize);
#ifdef MAXDEBUG
DBG_TRC(("Start service entered")); #endif
g_StiFileLog->ReportMessage(STI_TRACE_INFORMATION, MSG_TRACE_SVC_INIT,TEXT("STISVC"),0);
//
// Create shutdown event.
//
hShutdownEvent = CreateEvent( NULL, // lpsaSecurity
TRUE, // fManualReset
FALSE, // fInitialState
NULL ); // lpszEventName
if( hShutdownEvent == NULL ) { dwError = GetLastError(); return dwError; }
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
//
// Initialize active device list
//
InitializeDeviceList();
//
// Start RPC servicing
//
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
if (NOERROR != StartRpcServerListen()) { dwError = GetLastError(); DBG_ERR(("StiService failed to start RPC listen. ErrorCode=%d", dwError)); goto Cleanup; }
#ifdef WINNT
//
// Allow setting window to foreground
//
dwError = AllowSetForegroundWindow(GetCurrentProcessId()); // ASFW_ANY
DBG_TRC((" AllowSetForegroundWindow is called for id:%d . Ret code=%d. LastError=%d ", GetCurrentProcessId(), dwError, ::GetLastError())); #endif
//
// Create hidden window for receiving PnP notifications
//
if (!CreateServiceWindow()) { dwError = GetLastError(); DBG_ERR(("Failed to create hidden window for PnP notifications. ErrorCode=%d",dwError)); goto Cleanup; }
#ifdef WINNT
//
// Initialize NT security parameters
//
InitializeNTSecurity(); #endif
// No longer needed - the equivalent exists in CWiaDevMan
// g_pDeviceInfoSet = new DEVICE_INFOSET(GUID_DEVCLASS_IMAGE);
g_pDeviceInfoSet = NULL;
//
// Initiate device list refresh
//
//::PostMessage(g_hStiServiceWindow,
// STIMON_MSG_REFRESH,
// STIMON_MSG_REFRESH_REREAD,
// STIMON_MSG_REFRESH_NEW | STIMON_MSG_REFRESH_EXISTING
// | STIMON_MSG_BOOT // This shows this is the first device enumeration - no need to generate events
// );
//
// Finally we are running
//
g_fStiServiceInitialized = TRUE;
UpdateServiceStatus(SERVICE_RUNNING,NOERROR,0);
#ifdef MAXDEBUG
g_EventLog->LogEvent(MSG_STARTUP, 0, (LPCSTR *)NULL); #endif
g_StiFileLog->ReportMessage(STI_TRACE_INFORMATION,MSG_STARTUP);
return NOERROR;
Cleanup:
//
// Something failed , call stop routine to clean up
//
StiServiceStop();
return dwError;
} // StiServiceInitialize
VOID WINAPI StiServiceStop( VOID ) /*++
Routine Description:
Stopping STI service
Arguments:
Return Value:
None.
--*/ {
DBG_FN(StiServiceStop);
DBG_TRC(("Service is exiting"));
UpdateServiceStatus(SERVICE_STOP_PENDING,NOERROR,START_HINT);
#ifdef WINNT
//
// Clean up PnP notification handles
//
if (g_hStiServiceNotificationSink && g_hStiServiceNotificationSink!=INVALID_HANDLE_VALUE) { UnregisterDeviceNotification(g_hStiServiceNotificationSink); g_hStiServiceNotificationSink = NULL; }
for (UINT uiIndex = 0; (uiIndex < NOTIFICATION_GUIDS_NUM ); uiIndex++) { if (g_phDeviceNotificationsSinkArray[uiIndex] && (g_phDeviceNotificationsSinkArray[uiIndex]!=INVALID_HANDLE_VALUE)) { UnregisterDeviceNotification(g_phDeviceNotificationsSinkArray[uiIndex]); g_phDeviceNotificationsSinkArray[uiIndex] = NULL; } }
#endif
//
// Stop item scheduler
//
SchedulerSetPauseState(TRUE);
//
// Destroy service window
//
if (g_hStiServiceWindow) { DestroyWindow(g_hStiServiceWindow); g_hStiServiceWindow = NULL; }
#ifdef WINNT
//
// Free security objects
//
if(!TerminateNTSecurity()) { DBG_ERR(("Failed to clean up security objects")); } #endif
//
// Cancel all client calls
//
WiaEventNotifier *pOldWiaEventNotifier = g_pWiaEventNotifier; InterlockedCompareExchangePointer((VOID**)&g_pWiaEventNotifier, NULL, g_pWiaEventNotifier); if (pOldWiaEventNotifier) { delete pOldWiaEventNotifier; pOldWiaEventNotifier = NULL; }
// Since using AsyncRPC, we would rather just exit the process than shut the RPC
// server down. This is becuase even one outstanding AsyncRPC call
// will cause a hang attempting to stop the RPC server.
// It is much more preferable for us to just exit than introduce the
// possiblility of a hang, so we'll just exit and let the OS clean up for us.
//
// Stop RPC servicing
//
//if(NOERROR != StopRpcServerListen()) {
// DBG_ERR(("Failed to stop RpcServerListen"));
//}
//
// Terminate device list
//
TerminateDeviceList();
// Destroy info set
//if (g_pDeviceInfoSet) {
// delete g_pDeviceInfoSet;
// }
//
// Resume scheduling to allow for internal work items to complete
// At this point all device related items should've been purged by
// device object destructors
//
SchedulerSetPauseState(FALSE);
//
// Finish
//
g_fStiServiceInitialized = FALSE;
#ifdef MAXDEBUG
g_EventLog->LogEvent(MSG_STOP, 0, (LPCSTR *)NULL); #endif
//
// UnRegister the WiaDevice manager from the ROT
//
InitWiaDevMan(WiaUninitialize);
//
// Signal shutdown
//
SetEvent(hShutdownEvent);
//UpdateServiceStatus(SERVICE_STOPPED,NOERROR,0);
} // StiServiceStop
VOID WINAPI StiServicePause( VOID ) /*++
Routine Description:
Pausing STI service
Arguments:
Return Value:
None.
--*/ {
DBG_FN(StiServicePause);
//
// System is suspending - take snapshot of currently active devices
//
UpdateServiceStatus(SERVICE_PAUSE_PENDING,NOERROR,PAUSE_HINT);
if ( (g_StiServiceStatus.dwCurrentState == SERVICE_RUNNING) || (g_StiServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ){
// Stop running work items queue
//
// Nb: if refresh routine is scheduled to run as work item, this is a problem
//
SchedulerSetPauseState(TRUE);
/* The equivalent done by HandlePowerEvent
SendMessage(g_hStiServiceWindow, STIMON_MSG_REFRESH, STIMON_MSG_REFRESH_SUSPEND, STIMON_MSG_REFRESH_EXISTING ); */ }
} // StiServicePause
VOID WINAPI StiServiceResume( VOID ) /*++
Routine Description:
Resuming STI service
Arguments:
Return Value:
None.
--*/ {
DBG_FN(StiServiceResume);
SchedulerSetPauseState(FALSE);
PostMessage(g_hStiServiceWindow, STIMON_MSG_REFRESH, STIMON_MSG_REFRESH_RESUME, STIMON_MSG_REFRESH_NEW | STIMON_MSG_REFRESH_EXISTING );
UpdateServiceStatus(SERVICE_RUNNING,NOERROR,0);
} // StiServiceResume
ULONG WINAPI StiServiceCtrlHandler( IN DWORD dwOperation, DWORD dwEventType, PVOID EventData, PVOID pData ) /*++
Routine Description:
STI service control dispatch function
Arguments:
SCM OpCode
Return Value:
None. --*/ { ULONG retval = NO_ERROR; DBG_TRC(("Entering CtrlHandler OpCode=%d",dwOperation));
switch (dwOperation) { case SERVICE_CONTROL_STOP: StiServiceStop(); break;
case SERVICE_CONTROL_PAUSE: StiServicePause(); break;
case SERVICE_CONTROL_CONTINUE: StiServiceResume(); break;
case SERVICE_CONTROL_SHUTDOWN: StiServiceStop(); break;
case SERVICE_CONTROL_PARAMCHANGE: //
// Refresh device list.
//
g_pMsgHandler->HandleCustomEvent(SERVICE_CONTROL_PARAMCHANGE); break;
case SERVICE_CONTROL_INTERROGATE: // Report current state and status
UpdateServiceStatus(0,NOERROR,0); break;
case SERVICE_CONTROL_DEVICEEVENT: //
// PnP event.
//
//
// Until our PnP issues are resolved, keep logging PnP events so we know
// whether we received it or not...
//
DBG_WRN(("::StiServiceCtrlHandler, Received PnP event..."));
g_pMsgHandler->HandlePnPEvent(dwEventType, EventData); break;
case SERVICE_CONTROL_POWEREVENT: //
// Power management event
//
retval = g_pMsgHandler->HandlePowerEvent(dwEventType, EventData); break;
case STI_SERVICE_CONTROL_REFRESH:
//
// Refresh device list.
//
DBG_TRC(("::StiServiceCtrlHandler, Received STI_SERVICE_CONTROL_REFRESH")); g_pMsgHandler->HandleCustomEvent(STI_SERVICE_CONTROL_REFRESH);
break;
case STI_SERVICE_CONTROL_EVENT_REREAD: //
// Refresh device list.
//
DBG_TRC(("::StiServiceCtrlHandler, Received STI_SERVICE_CONTROL_EVENT_REREAD")); g_pMsgHandler->HandleCustomEvent(STI_SERVICE_CONTROL_EVENT_REREAD);
break;
case STI_SERVICE_CONTROL_LPTENUM:
//
// Enumerate LPT port.
//
EnumLpt(); break;
default: // Unknown opcode
; }
DBG_TRC(("Exiting CtrlHandler"));
return retval;
} // StiServiceCtrlHandler
BOOL RegisterServiceControlHandler() { DWORD dwError = 0;
#ifdef WINNT
g_StiServiceStatusHandle = RegisterServiceCtrlHandlerEx( STI_SERVICE_NAME, StiServiceCtrlHandler, (LPVOID)STI_SERVICE__DATA ); if(!g_StiServiceStatusHandle) { // Could not register with SCM
dwError = GetLastError(); DBG_ERR(("Failed to register CtrlHandler,ErrorCode=%d",dwError)); return FALSE; }
#endif
return TRUE; }
VOID WINAPI StiServiceMain( IN DWORD argc, IN LPTSTR *argv ) /*++
Routine Description:
This is service main entry, that is called by SCM
Arguments:
Return Value:
None.
--*/ { DWORD dwError; DEV_BROADCAST_DEVICEINTERFACE PnPFilter;
DBG_FN(StiServiceMain); #ifdef MAXDEBUG
DBG_TRC(("StiServiceMain entered")); #endif
//
// REMOVE: This is not actually an error, but we will use error logging to gurantee
// it always get written to the log. This should be removed as soon as we know what
// causes #347835.
//
SYSTEMTIME SysTime; GetLocalTime(&SysTime); DBG_ERR(("*> StiServiceMain entered, Time: %d/%02d/%02d %02d:%02d:%02d:%02d", SysTime.wYear, SysTime.wMonth, SysTime.wDay, SysTime.wHour, SysTime.wMinute, SysTime.wSecond, SysTime.wMilliseconds));
g_StiServiceStatus.dwServiceType = STI_SVC_SERVICE_TYPE; g_StiServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_StiServiceStatus.dwControlsAccepted= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PARAMCHANGE | SERVICE_ACCEPT_POWEREVENT;
g_StiServiceStatus.dwWin32ExitCode = NO_ERROR; g_StiServiceStatus.dwServiceSpecificExitCode = NO_ERROR; g_StiServiceStatus.dwCheckPoint = 0; g_StiServiceStatus.dwWaitHint = 0;
dwError = StiServiceInitialize();
if (NOERROR == dwError) {
#ifdef WINNT
if (g_fUseServiceCtrlSink && !g_hStiServiceNotificationSink) {
DBG_WRN(("::StiServiceMain, About to register for PnP..."));
//
// Register for the PnP Device Interface change notifications
//
memset(&PnPFilter, 0, sizeof(PnPFilter)); PnPFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); PnPFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; PnPFilter.dbcc_reserved = 0x0; PnPFilter.dbcc_classguid = *g_pguidDeviceNotificationsGuid;
//memcpy(&PnPFilter.dbcc_classguid,
// (LPGUID) g_pguidDeviceNotificationsGuid,
// sizeof(GUID));
g_hStiServiceNotificationSink = RegisterDeviceNotification( (HANDLE) g_StiServiceStatusHandle, &PnPFilter, DEVICE_NOTIFY_SERVICE_HANDLE ); if (NULL == g_hStiServiceNotificationSink) { //
// Could not register with PnP - attempt to use window handle
//
g_fUseServiceCtrlSink = FALSE; }
//
// Separately from main Image interface , register list of optional device interfaces
// we will monitor to allow parameters refresh.
//
for (UINT uiIndex = 0; (uiIndex < NOTIFICATION_GUIDS_NUM ) && (!::IsEqualGUID(g_pguidDeviceNotificationsGuidArray[uiIndex],GUID_NULL)); uiIndex++) { ::ZeroMemory(&PnPFilter, sizeof(PnPFilter));
PnPFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); PnPFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; PnPFilter.dbcc_reserved = 0x0; PnPFilter.dbcc_classguid = g_pguidDeviceNotificationsGuidArray[uiIndex];
g_phDeviceNotificationsSinkArray[uiIndex] = RegisterDeviceNotification( (HANDLE) g_StiServiceStatusHandle, &PnPFilter, DEVICE_NOTIFY_SERVICE_HANDLE );
DBG_TRC(("Registering optional interface #%d . Returned handle=%X", uiIndex,g_phDeviceNotificationsSinkArray[uiIndex])); } }
#else
// Windows 98 case
g_fUseServiceCtrlSink = FALSE;
#endif
//
// Service initialized , process command line arguments
//
BOOL fVisualize = FALSE; BOOL fVisualizeRequest = FALSE; TCHAR cOption; UINT iCurrentOption = 0;
for (iCurrentOption=0; iCurrentOption < argc ; iCurrentOption++ ) {
cOption = *argv[iCurrentOption]; // pszT = argv[iCurrentOption]+ 2 * sizeof(TCHAR);
switch ((TCHAR)LOWORD(::CharUpper((LPTSTR)cOption))) { case 'V': fVisualizeRequest = TRUE; fVisualize = TRUE; break; case 'H': fVisualizeRequest = TRUE; fVisualize = FALSE; break; default: break; }
if (fVisualizeRequest ) { VisualizeServer(fVisualizeRequest); } }
//
// Wait for shutdown processing messages. We make ourselves alertable so we
// can receive Shell's Volume notifications via APCs. If we're woken
// up to process the APC, then we must wait again.
//
while(WaitForSingleObjectEx(hShutdownEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION);
#ifndef WINNT
//Don't use windows messaging on NT
//
// Close down message pump
//
if (g_dwMessagePumpThreadId) {
// Indicate we are entering shutdown
g_fServiceInShutdown = TRUE;
PostThreadMessage(g_dwMessagePumpThreadId, WM_QUIT, 0, 0L ); } #endif
CloseHandle( hShutdownEvent ); hShutdownEvent = NULL; } else { // Could not initialize service, service failed to start
}
//
// REMOVE: This is not actually an error, but we will use error logging to gurantee
// it always get written to the log. This should be removed as soon as we know what
// causes #347835.
//
GetLocalTime(&SysTime); DBG_ERR(("<* StiServiceMain ended, Time: %d/%02d/%02d %02d:%02d:%02d:%02d", SysTime.wYear, SysTime.wMonth, SysTime.wDay, SysTime.wHour, SysTime.wMinute, SysTime.wSecond, SysTime.wMilliseconds)); return;
} // StiServiceMain
HWND WINAPI CreateServiceWindow( VOID ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{ #ifndef WINNT
//Don't use windows messaging on NT
WNDCLASSEX wc; DWORD dwError; HWND hwnd = FindWindow(g_szStiSvcClassName,NULL);
// Window should NOT exist at this time
if (hwnd) { DPRINTF(DM_WARNING ,TEXT("Already registered window")); return NULL; }
//
// Create class
//
ZeroMemory(&wc, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_GLOBALCLASS; wc.lpfnWndProc = StiSvcWinProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1); wc.lpszClassName = g_szStiSvcClassName; wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
if (!RegisterClassEx(&wc)) {
dwError = GetLastError(); if (dwError != ERROR_CLASS_ALREADY_EXISTS) { DBG_ERR(("Failed to register window class ErrorCode=%d",dwError)); return NULL; } }
#ifndef WINNT
#ifdef FE_IME
// Disable IME processing on Millenium
ImmDisableIME(::GetCurrentThreadId()); #endif
#endif
g_hStiServiceWindow = CreateWindowEx(0, // Style bits
g_szStiSvcClassName, // Class name
g_szTitle, // Title
WS_DISABLED , // Window style bits
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // h
CW_USEDEFAULT, // w
NULL, // Parent
NULL, // Menu
g_hInst, // Module instance
NULL); // Options
if (!g_hStiServiceWindow) { dwError = GetLastError(); DBG_ERR(("Failed to create PnP window ErrorCode=%d"),dwError); }
#else
g_hStiServiceWindow = (HWND) INVALID_HANDLE_VALUE; #endif
return g_hStiServiceWindow;
} // CreateServiceWindow
//
// Installation routines.
// They are here to simplify debugging and troubleshooting, called by switches
// on command line
//
DWORD WINAPI StiServiceInstall( LPTSTR lpszUserName, LPTSTR lpszUserPassword ) /*++
Routine Description:
Service installation function. Calls SCM to install STI service, which is running in user security context
Arguments:
Return Value:
None.
--*/ {
DWORD dwError = NOERROR;
#ifdef WINNT
SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL;
TCHAR szDisplayName[MAX_PATH];
//
// Write the svchost group binding to stisvc
//
RegEntry SvcHostEntry(STI_SVC_HOST, HKEY_LOCAL_MACHINE); TCHAR szValue[MAX_PATH]; lstrcpy (szValue, STI_SERVICE_NAME); // REG_MULTI_SZ is double null terminated
*(szValue+lstrlen(szValue)+1) = TEXT('\0'); SvcHostEntry.SetValue(STI_IMGSVC, STI_SERVICE_NAME, REG_MULTI_SZ);
#endif // winnt
//
// Write parameters key for svchost
//
TCHAR szMyPath[MAX_PATH] = {0}; TCHAR szSvcPath[MAX_PATH] = SYSTEM_PATH; LONG lLen; LONG lNameIndex = 0;
if (lLen = ::GetModuleFileName(g_hInst, szMyPath, sizeof(szMyPath)/sizeof(szMyPath[0]) - 1)) {
RegEntry SvcHostParm(STI_SERVICE_PARAMS, HKEY_LOCAL_MACHINE);
//
// Get the name of the service file (not including the path)
//
for (lNameIndex = lLen; lNameIndex > 0; lNameIndex--) { if (szMyPath[lNameIndex] == '\\') { lNameIndex++; break; } }
if (lNameIndex) {
#ifndef WINNT
//
// Windows 98 specific entry
//
TCHAR szWinDir[MAX_PATH] = TEXT("\0");
if (!GetWindowsDirectory(szWinDir, MAX_PATH)) { DPRINTF(DM_ERROR ,TEXT("Error extracting Still Image service filename.")); return dwError; }
lstrcat(szWinDir, SYSTEM_PATH); lstrcpy(szSvcPath, szWinDir); #endif
lstrcat(szSvcPath, &szMyPath[lNameIndex]); SvcHostParm.SetValue(REGSTR_SERVICEDLL, szSvcPath, PATH_REG_TYPE);
} else { DBG_ERR(("Error extracting Still Image service filename.")); } } else { DBG_ERR(("Failed to get my own path registering Still Image service . LastError=%d", ::GetLastError())); }
// Add registry settings for event logging
RegisterStiEventSources();
return dwError;
} //StiServiceInstall
DWORD WINAPI StiServiceRemove( VOID )
/*++
Routine Description:
Service removal function. This function calls SCM to remove the STI service.
Arguments:
None.
Return Value:
Return code. Return zero for success
--*/
{ DWORD dwError = NOERROR;
#ifdef WINNT
SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL;
SERVICE_STATUS ServiceStatus; UINT uiRetry = 10;
__try {
hSCM = ::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (!hSCM) { dwError = GetLastError(); __leave; }
hService = OpenService( hSCM, STI_SERVICE_NAME, SERVICE_ALL_ACCESS ); if (!hService) { dwError = GetLastError(); __leave; }
//
// Stop service first
//
if (ControlService( hService, SERVICE_CONTROL_STOP, &ServiceStatus )) { //
// Wait a little
//
Sleep( STI_STOP_FOR_REMOVE_TIMEOUT );
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
while( QueryServiceStatus( hService, &ServiceStatus ) && (SERVICE_STOP_PENDING == ServiceStatus.dwCurrentState)) { Sleep( STI_STOP_FOR_REMOVE_TIMEOUT ); if (!uiRetry--) { break; } }
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED) { dwError = GetLastError(); __leave; } } else { dwError = GetLastError();
//
// ERROR_SERVICE_NOT_ACTIVE is fine, since it means that the service has
// already been stopped. If the error is not ERROR_SERVICE_NOT_ACTIVE then
// something has gone wrong, so __leave.
//
if (dwError != ERROR_SERVICE_NOT_ACTIVE) { __leave; } }
if (!DeleteService( hService )) { dwError = GetLastError(); __leave; } else { DBG_TRC(("StiServiceRemove, removed STI service")); } } __finally { CloseServiceHandle( hService ); CloseServiceHandle( hSCM ); }
#endif
return dwError;
} // StiServiceRemove
VOID SvchostPushServiceGlobals( PSVCHOST_GLOBAL_DATA pGlobals ) {
//
// For now, we do nothing here. We will need to revisit when we run under
// a shared SvcHost Group.
//
return; }
|