这里有windows nt service 例子的源代码:
http://codeguru.earthweb.com/system/index.shtml
http://codeguru.earthweb.com/system/index.shtml
解决方案 »
- MFC小程序一运行就退出是什么原因?
- 如果在屏幕上的某个区域显示一个图片并移动?
- 碰到一个超级难搞定的问题,如果哪位大侠能够提供解决思路,小弟我感激不敬!为什么我的程序在一台机器上能够顺利退出,但是在另一台机器
- formview 的背景色怎么设?
- LionTang(唐旭) 请进,谢谢你的帮助!
- 谁有关于OPC方面的资料吗?
- sos:谁有水平滚动字模类
- 辅助线程无法调用对话框的UpdateData函数
- 河南籍丈夫疑妻不忠将其杀死 剥光其衣服检查性病
- 向大家讨教一下如何编程获取内存SPD信息?
- 实在没分了,可还有问题,怎样在树列表控件中给item添加图标(
- Imagelist attached to the listview ctrl (Activex control)
能不能给我一份,[email protected]
#ifndef NTService_h
#define NTService_h
class CNTService {
static BOOL m_bInstance; // only one CNTService object per application
protected: // data members
LPCTSTR m_lpServiceName;
LPCTSTR m_lpDisplayName;
DWORD m_dwCheckPoint;
DWORD m_dwErr;
BOOL m_bDebug; // TRUE if -d was passed to the program
SERVICE_STATUS m_ssStatus; // current status of the service
SERVICE_STATUS_HANDLE m_sshStatusHandle;
DWORD m_dwControlsAccepted; // bit-field of what control requests the
// service will accept
// (dflt: SERVICE_ACCEPT_STOP)
PSID m_pUserSID; // the current user's security identifier
BOOL m_bWinNT; // TRUE, if this is running on WinNT FALSE on Win95
BOOL m_fConsoleReady; // parameters to the "CreateService()" function:
DWORD m_dwDesiredAccess; // default: SERVICE_ALL_ACCESS
DWORD m_dwServiceType; // default: SERVICE_WIN32_OWN_PROCESS
DWORD m_dwStartType; // default: SERVICE_AUTO_START
DWORD m_dwErrorControl; // default: SERVICE_ERROR_NORMAL
LPCTSTR m_pszLoadOrderGroup; // default: NULL
DWORD m_dwTagID; // retrieves the tag identifier
LPCTSTR m_pszDependencies; // default: NULL
LPCTSTR m_pszStartName; // default: NULL
LPCTSTR m_pszPassword; // default: NULL
public: // construction/destruction
// If <DisplayName> is not set, then it defaults to <ServiceName>.
CNTService(LPCTSTR ServiceName, LPCTSTR DisplayName = 0);
~CNTService(); private: // forbidden functions
CNTService( const CNTService & );
CNTService & operator=( const CNTService & ); public: // overridables
// You have to override the following two functions.
// "Run()" will be called to start the real
// service's activity. You must call
// ReportStatus(SERVICE_RUNNING);
// before you enter your main-loop !
// "Stop()" will be called to stop the work of
// the service. You should break out of the mainloop
// and return control to the CNTService class.
//
// In most cases these functions look like these:
//
// void CMyService :: Run(DWORD argc, LPTSTR * argv) {
// ReportStatus(SERVICE_START_PENDING);
// // do some parameter processing ...
// ReportStatus(SERVICE_START_PENDING);
// // do first part of initialisation ...
// ReportStatus(SERVICE_START_PENDING);
// // do next part of initialisation
// // ...
// m_hStop = CreateEvent(0, TRUE, FALSE, 0);
// ReportStatus(SERVICE_RUNNING);
// while( WaitForSingleObject(m_hStop, 10) != WAIT_OBJECT_0 ) {
// // do something
// }
// if( m_hStop )
// CloseHandle(m_hStop);
// }
//
// void CMyService :: Stop() {
// if( m_hStop )
// SetEvent(m_hStop);
// ReportStatus(SERVICE_STOP_PENDING);
// }
virtual void Run(DWORD argc, LPTSTR * argv) = 0;
virtual void Stop() = 0; // Pause() and Continue() do nothing by default.
// You can override them to handle a control request.
// Pause() should report the status SERVICE_PAUSED
// and Continue() should report the status SERVICE_RUNNING
// (see ReportStatus() below).
// Note that normally these functions will never be called. If
// you want a service, that accepts PAUSE and CONTINUE control
// requests, you have to to add SERVICE_ACCEPT_PAUSE_CONTINUE
// to the m_dwControlsAccepted data member.
virtual void Pause();
virtual void Continue(); // Shutdown() will be called, if the service manager
// requests for the SERVICE_CONTROL_SHUTDOWN control.
// This control type occurs, when the system shuts down.
// If you want to process this notification, you have to
// add SERVICE_ACCEPT_SHUTDOWN to the m_dwControlsAccepted
// data member (and to override this function). The default
// implementation of Shutdown() does nothing.
virtual void Shutdown(); // Call "RegisterService()" after you have constructed
// a CNTService object:
// A typical "main()" function of a service looks like this:
//
// int main( int argc, char ** argv ) {
// CMyService serv;
// exit(serv.RegisterService(argc, argv));
// }
//
// Where "CMyService" is a CNTService derived class.
// RegisterService() checks the parameterlist. The
// following parameters will be detected:
// -i install the service (calls
// "InstallService()" - see below)
//
// -l <account>
// <account> is the name of a user,
// under which the service shall run.
// This option is useful with -i only.
// <account> needs to have the advanced
// user-right "Log on as a service"
// (see User-Manager)
// <account> should have the following
// format: "<Domain>\<user>"
// "EuroS2Team\jko" for instance.
// The domain "." is predefined as the
// local machine. So one might use
// ".\jko" too.
//
// -p <password>
// The password of the user, under which
// the service shall run. Only useful
// with -i and -l together.
//
// -u uninstall the service (calls
// "RemoveService()" - see below)
//
// -d debug the service (run as console
// process; calls "DebugService()"
// see below)
//
// -e end the service (if it is running)
//
// -s start the service (if it is not running)
// (Note that *you* normally cannot start
// an NT-service from the command-line.
// The SCM can.)
//
// Do not use -l and -p, if your service is of type
// SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER.
// Furthermore you canot use -i and -s together. Instead
// you have to start the command twice, first you install
// the service, then you start it.
// If none of the flags -i, -u, -e, -s and -d is set, then the
// program starts as an NT service (only the SCM can start it
// this way!).
// NOTE: If UNICODE is #define'd, then <argc> and <argv>
// will be ignored and the original commandline
// of the program will be used to parse the
// arguments !
virtual BOOL RegisterService(int argc, char ** argv);
// "StartDispatcher()" registers one service-procedure
// to the service control dispatcher (using the predefined
// "ServiceMain()" function below).
// Override this funtion, if you want to develop a
// multithreaded NT-Service.
virtual BOOL StartDispatcher(); // Override "InstallService()" to manipulate the
// installation behavior.
// This function will only be called, if the
// "-i" flag was passed to "RegisterService()"
// (see above)
// After "InstallService()" has completed, you
// should be able to see the service in the
// "services" control-panel-applet.
virtual BOOL InstallService();
// RemoveService() removes a service from the system's
// service-table.
// It first tries to stop the service.
// This function will be called only if the -u
// flag was passed to the program. (see "RegisterService()"
// above)
// After removal of the service, it should no longer
// appear in the "services" control-panel-applet.
virtual BOOL RemoveService();
// EndService() stops a running service (if the service
// is running as a service! Does not end a service
// running as a console program (see DebugService()
// below))
virtual BOOL EndService(); // Start the service. Does the same as if the
// SCM launches the program. Note that this method
// will create a new instance of the program.
virtual BOOL StartupService();
// Run a service as a console application. This makes it
// easier to debug the service.
// This function will be called only if the -d flag
// was passed to the program(see "RegisterService()" above).
// It transparently calls "Run()". You can simulate a
// stop-request by pressing either Ctrl-C or Ctrl-Break (that
// will call the "Stop()" method).
virtual BOOL DebugService(int argc, char **argv,BOOL faceless=FALSE); //!! TCW MOD - added FACELESS parm to allow Win95 usage. protected: // implementation
// Override "RegisterApplicationLog()", if you want to register
// a different message file and/or differend supported types
// than the default.
// The proposed message file is the application itself.
// The proposed types are:
// EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE
// This method will be called from inside "InstallService()" (see above)
// Thus if you support errors only (for instance):
// void CMyService :: RegisterApplicationLog(LPCTSTR filename, DWORD ) {
// CNTService::RegisterApplicationLog(filename, EVENTLOG_ERROR_TYPE);
// }
// This method will never be called on Win95.
virtual void RegisterApplicationLog(
LPCTSTR lpszProposedMessageFile,
DWORD dwProposedTypes
); // "DeregisterApplicationLog()" is called from inside "RemoveService()"
// (see above) to clear the registry-entries made by
// "RegisterApplicationLog()"
virtual void DeregisterApplicationLog(); public: // helpers
// Retrieve a human-readable error message. The message
// will be stored in <Buf> which is of size <Size>.
// Returns a pointer to <Buf>.
LPTSTR GetLastErrorText(LPTSTR Buf, DWORD Size); // report status to the service-control-manager.
// <CurState> can be one of:
// SERVICE_START_PENDING - the service is starting
// SERVICE_RUNNING - the service is running
// SERVICE_STOP_PENDING - the service is stopping
// SERVICE_STOPPED - the service is not running
// SERVICE_PAUSE_PENDING - the service pause is pending
// SERVICE_PAUSE - the service is paused
// SERVICE_CONTINUE_PENDING - the service is about to continue
BOOL ReportStatus(
DWORD CurState, // service's state
DWORD WaitHint = 3000, // expected time of operation in milliseconds
DWORD ErrExit = 0 //!! TCW MOD - set to nonzero to flag *FATAL* error
); // AddToMessageLog() writes a message to the application event-log.
// (use EventViewer from the menu "Administrative Tools" to watch the log).
// The <EventType> parameter can be set to one of the following values:
// EVENTLOG_ERROR_TYPE Error event
// EVENTLOG_WARNING_TYPE Warning event
// EVENTLOG_INFORMATION_TYPE Information event
// EVENTLOG_AUDIT_SUCCESS Success Audit event
// EVENTLOG_AUDIT_FAILURE Failure Audit event
// See "ReportEvent()" in the help-topics for further information.
virtual void AddToMessageLog(
LPTSTR Message,
WORD EventType = EVENTLOG_ERROR_TYPE,
DWORD dwEventID = DWORD(-1)
); public: // default handlers
// The following functions will be used by default.
// You can provide other handlers. If so, you have to
// overload several of the "virtual"s above.
static void WINAPI ServiceCtrl(DWORD CtrlCode);
static void WINAPI ServiceMain(DWORD argc, LPTSTR * argv);
static BOOL WINAPI ControlHandler(DWORD CtrlType); //!! TCW MOD - added console support for Faceless Apps. Needed sometimes when something goes wrong.
public:
BOOL OsIsWin95() const { return ! m_bWinNT; }
void SetupConsole();};
// Retrieve the one and only CNTService object:
CNTService * AfxGetService();
#endif // NTService_h
//-------------------------------NTService.cpp--------------------------------------------
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <crtdbg.h>#include <io.h> //!! TCW MOD
#include <fcntl.h> //!! TCW MOD#include "NTService.h"
#include "NTServiceEventLogMsg.h"
#ifndef RSP_SIMPLE_SERVICE
#define RSP_SIMPLE_SERVICE 1
#endif
#ifndef RSP_UNREGISTER_SERVICE
#define RSP_UNREGISTER_SERVICE 0
#endifBOOL CNTService :: m_bInstance = FALSE;static CNTService * gpTheService = 0; // the one and only instanceCNTService * AfxGetService() { return gpTheService; }
static LPCTSTR gszAppRegKey = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
static LPCTSTR gszWin95ServKey=TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"); //!! TCW MOD
/////////////////////////////////////////////////////////////////////////////
// class CNTService -- construction/destructionCNTService :: CNTService( LPCTSTR lpServiceName, LPCTSTR lpDisplayName )
: m_lpServiceName(lpServiceName)
, m_lpDisplayName(lpDisplayName ? lpDisplayName : lpServiceName)
, m_dwCheckPoint(0)
, m_dwErr(0)
, m_bDebug(FALSE)
, m_sshStatusHandle(0)
, m_dwControlsAccepted(SERVICE_ACCEPT_STOP)
, m_pUserSID(0)
, m_fConsoleReady(FALSE)
// parameters to the "CreateService()" function:
, m_dwDesiredAccess(SERVICE_ALL_ACCESS)
, m_dwServiceType(SERVICE_WIN32_OWN_PROCESS)
, m_dwStartType(SERVICE_AUTO_START)
, m_dwErrorControl(SERVICE_ERROR_NORMAL)
, m_pszLoadOrderGroup(0)
, m_dwTagID(0)
, m_pszDependencies(0)
, m_pszStartName(0)
, m_pszPassword(0)
{
_ASSERTE( ! m_bInstance ); OSVERSIONINFO vi;
vi.dwOSVersionInfoSize=sizeof(vi); // init this.
GetVersionEx(&vi); //lint !e534
m_bWinNT = (vi.dwPlatformId == VER_PLATFORM_WIN32_NT); m_bInstance = TRUE;
gpTheService = this;
// SERVICE_STATUS members that rarely change
m_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_ssStatus.dwServiceSpecificExitCode = 0; if( m_bWinNT ) {
/////////////////////////////////////////////////////////////////////////
// Providing a SID (security identifier) was contributed by Victor
// Vogelpoel ([email protected]).
// The code from Victor was slightly modified. // Get security information of current user
BYTE security_identifier_buffer[ 4096 ];
DWORD dwSizeSecurityIdBuffer = sizeof( security_identifier_buffer );
PSID user_security_identifier = NULL; TCHAR sUserName[ 256 ];
DWORD dwSizeUserName = 255; TCHAR sDomainName[ 256 ];
DWORD dwSizeDomainName = 255; SID_NAME_USE sidTypeSecurityId; ::ZeroMemory( sUserName, sizeof( sUserName ) );
::ZeroMemory( sDomainName, sizeof( sDomainName ) );
::ZeroMemory( security_identifier_buffer, dwSizeSecurityIdBuffer ); ::GetUserName( sUserName, &dwSizeUserName ); if( ::LookupAccountName(
0,
sUserName,
&security_identifier_buffer,
&dwSizeSecurityIdBuffer,
sDomainName,
&dwSizeDomainName,
&sidTypeSecurityId
)) {
if( ::IsValidSid( PSID(security_identifier_buffer) ) ) {
DWORD dwSidLen = ::GetLengthSid(PSID(security_identifier_buffer));
m_pUserSID = PSID(new BYTE [dwSidLen]);
::CopySid(dwSidLen, m_pUserSID, security_identifier_buffer);
_ASSERTE(::EqualSid(m_pUserSID, security_identifier_buffer));
}
}
}
/////////////////////////////////////////////////////////////////////////
}
CNTService :: ~CNTService() {
_ASSERTE( m_bInstance );
delete [] LPBYTE(m_pUserSID);
m_bInstance = FALSE;
gpTheService = 0;
}/////////////////////////////////////////////////////////////////////////////
// class CNTService -- overridables#define NEXT_ARG ((((*Argv)[2])==TEXT('\0'))?(--Argc,*++Argv):(*Argv)+2)
BOOL CNTService :: RegisterService( int argc, char ** argv ) {
BOOL (CNTService::* fnc)() = &CNTService::StartDispatcher;
DWORD Argc;
LPTSTR * Argv;#ifdef UNICODE
Argv = CommandLineToArgvW(GetCommandLineW(), &Argc );
#else
Argc = (DWORD) argc;
Argv = argv;
#endif while( ++Argv, --Argc ) {
if( Argv[0][0] == TEXT('-') ) {
switch( Argv[0][1] ) {
case TEXT('i'): // install the service
fnc = &CNTService::InstallService;
break;
case TEXT('l'): // login-account (only useful with -i)
m_pszStartName = NEXT_ARG;
break;
case TEXT('p'): // password (only useful with -i)
m_pszPassword = NEXT_ARG;
break;
case TEXT('u'): // uninstall the service
fnc = &CNTService::RemoveService;
break;
case TEXT('s'): // start the service
fnc = &CNTService::StartupService;
break;
case TEXT('e'): // end the service
fnc = &CNTService::EndService;
break;
case TEXT('d'): // debug the service
case TEXT('f'): //!! TCW MOD faceless non-service (Win95) mode
#ifdef UNICODE
::GlobalFree(HGLOBAL)Argv);
#endif m_bDebug = TRUE;
// pass original parameters to DebugService()
return DebugService(argc, argv,(Argv[0][1]==TEXT('f'))); //!! TCW MOD faceless non-service (Win95) mode
}
}
}#ifdef UNICODE
::GlobalFree(HGLOBAL)Argv);
#endif //!! TCW MOD START - if Win95, run as faceless app.
if( fnc == &CNTService::StartDispatcher && OsIsWin95() ) {
// act as if -f was passed anyways.
m_bDebug = TRUE;
return DebugService(argc, argv, TRUE);
}
//!! TCW MOD END - if Win95, run as faceless app. return (this->*fnc)();
}
BOOL CNTService :: StartDispatcher() {
// Default implementation creates a single threaded service.
// Override this method and provide more table entries for
// a multithreaded service (one entry for each thread).
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ LPTSTR(m_lpServiceName), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ 0, 0 }
}; BOOL bRet = StartServiceCtrlDispatcher(dispatchTable);
if( ! bRet ) {
TCHAR szBuf[256];
AddToMessageLog(GetLastErrorText(szBuf,255));
} return bRet;
}
BOOL CNTService :: InstallService() {
TCHAR szPath[1024]; SetupConsole(); //!! TCW MOD - have to show the console here for the
// diagnostic or error reason: orignal class assumed
// that we were using _main for entry (a console app).
// This particular usage is a Windows app (no console),
// so we need to create it. Using SetupConsole with _main
// is ok - does nothing, since you only get one console. if( GetModuleFileName( 0, szPath, 1023 ) == 0 ) {
TCHAR szErr[256];
_tprintf(TEXT("Unable to install %s - %s\n"), m_lpDisplayName, GetLastErrorText(szErr, 256));
return FALSE;
} BOOL bRet = FALSE; if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
// Create a key for that application and insert values for
// "EventMessageFile" and "TypesSupported"
HKEY hKey = 0;
LONG lRet = ERROR_SUCCESS;
if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
lRet = ::RegSetValueEx(
hKey, // handle of key to set value for
m_lpServiceName, // address of value to set (NAME OF SERVICE)
0, // reserved
REG_EXPAND_SZ, // flag for value type
(CONST BYTE*)szPath,// address of value data
_tcslen(szPath) + 1 // size of value data
);
::RegCloseKey(hKey);
bRet=TRUE;
}
} else {
// Real NT services go here.
SC_HANDLE schSCManager = OpenSCManager(
0, // machine (NULL == local)
0, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if( schSCManager ) {
SC_HANDLE schService = CreateService(
schSCManager,
m_lpServiceName,
m_lpDisplayName,
m_dwDesiredAccess,
m_dwServiceType,
m_dwStartType,
m_dwErrorControl,
szPath,
m_pszLoadOrderGroup,
((m_dwServiceType == SERVICE_KERNEL_DRIVER ||
m_dwServiceType == SERVICE_FILE_SYSTEM_DRIVER) &&
(m_dwStartType == SERVICE_BOOT_START ||
m_dwStartType == SERVICE_SYSTEM_START)) ?
&m_dwTagID : 0,
m_pszDependencies,
m_pszStartName,
m_pszPassword
); if( schService ) {
_tprintf(TEXT("%s installed.\n"), m_lpDisplayName );
CloseServiceHandle(schService);
bRet = TRUE;
} else {
TCHAR szErr[256];
_tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
} CloseServiceHandle(schSCManager);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
} if( bRet ) {
// installation succeeded. Now register the message file
RegisterApplicationLog(
szPath, // the path to the application itself
EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE // supported types
); AddToMessageLog(TEXT("Service installed"),EVENTLOG_INFORMATION_TYPE);
}
} //!! TCW MOD return bRet;
}
BOOL CNTService :: RemoveService() {
BOOL bRet = FALSE; SetupConsole(); //!! TCW MOD - have to show the console here for the
// diagnostic or error reason: orignal class assumed
// that we were using _main for entry (a console app).
// This particular usage is a Windows app (no console),
// so we need to create it. Using SetupConsole with _main
// is ok - does nothing, since you only get one console.
if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
HKEY hKey = 0;
LONG lRet = ERROR_SUCCESS;
if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
lRet = ::RegDeleteValue(hKey, m_lpServiceName);
::RegCloseKey(hKey);
bRet=TRUE;
}
} else {
// Real NT services go here.
SC_HANDLE schSCManager = OpenSCManager(
0, // machine (NULL == local)
0, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if( schSCManager ) {
SC_HANDLE schService = OpenService(
schSCManager,
m_lpServiceName,
SERVICE_ALL_ACCESS
); if( schService ) {
// try to stop the service
if( ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
_tprintf(TEXT("Stopping %s."), m_lpDisplayName);
Sleep(1000); while( QueryServiceStatus(schService, &m_ssStatus) ) {
if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
_tprintf(TEXT("."));
Sleep( 1000 );
} else
break;
} if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
_tprintf(TEXT("\n%s stopped.\n"), m_lpDisplayName);
else
_tprintf(TEXT("\n%s failed to stop.\n"), m_lpDisplayName);
} // now remove the service
if( DeleteService(schService) ) {
_tprintf(TEXT("%s removed.\n"), m_lpDisplayName);
bRet = TRUE;
} else {
TCHAR szErr[256];
_tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
} CloseServiceHandle(schService);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
} CloseServiceHandle(schSCManager);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
} if( bRet )
DeregisterApplicationLog();
} return bRet;
}
BOOL CNTService :: EndService() {
BOOL bRet = FALSE; SC_HANDLE schSCManager = ::OpenSCManager(
0, // machine (NULL == local)
0, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if( schSCManager ) {
SC_HANDLE schService = ::OpenService(
schSCManager,
m_lpServiceName,
SERVICE_ALL_ACCESS
); if( schService ) {
// try to stop the service
if( ::ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
_tprintf(TEXT("Stopping %s."), m_lpDisplayName);
::Sleep(1000); while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
_tprintf(TEXT("."));
::Sleep( 1000 );
} else
break;
} if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
bRet = TRUE, _tprintf(TEXT("\n%s stopped.\n"), m_lpDisplayName);
else
_tprintf(TEXT("\n%s failed to stop.\n"), m_lpDisplayName);
} ::CloseServiceHandle(schService);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
} ::CloseServiceHandle(schSCManager);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
} return bRet;
}
BOOL CNTService :: StartupService() {
BOOL bRet = FALSE; SC_HANDLE schSCManager = ::OpenSCManager(
0, // machine (NULL == local)
0, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if( schSCManager ) {
SC_HANDLE schService = ::OpenService(
schSCManager,
m_lpServiceName,
SERVICE_ALL_ACCESS
); if( schService ) {
// try to start the service
_tprintf(TEXT("Starting up %s."), m_lpDisplayName);
if( ::StartService(schService, 0, 0) ) {
Sleep(1000); while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
if( m_ssStatus.dwCurrentState == SERVICE_START_PENDING ) {
_tprintf(TEXT("."));
Sleep( 1000 );
} else
break;
} if( m_ssStatus.dwCurrentState == SERVICE_RUNNING )
bRet = TRUE, _tprintf(TEXT("\n%s started.\n"), m_lpDisplayName);
else
_tprintf(TEXT("\n%s failed to start.\n"), m_lpDisplayName);
} else {
// StartService failed
TCHAR szErr[256];
_tprintf(TEXT("\n%s failed to start: %s\n"), m_lpDisplayName, GetLastErrorText(szErr,256));
} ::CloseServiceHandle(schService);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
} ::CloseServiceHandle(schSCManager);
} else {
TCHAR szErr[256];
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
} return bRet;
}
////////////////////////////////////////////////////////////////////////////
//!! TCW MOD - faceless window procedure for usage within Win95 (mostly),
// but can be invoked under NT by using -f
LRESULT CALLBACK _FacelessWndProc_( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
if (uMsg==WM_QUERYENDSESSION || uMsg==WM_ENDSESSION || uMsg==WM_QUIT) {
if (lParam==NULL || uMsg==WM_QUIT) {
DestroyWindow(hwnd); // kill me
if (AfxGetService()!=NULL)
AfxGetService()->Stop(); // stop me.
return TRUE;
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
////////////////////////////////////////////////////////////////////////////
BOOL CNTService :: DebugService(int argc, char ** argv, BOOL faceless) {
DWORD dwArgc;
LPTSTR *lpszArgv;#ifdef UNICODE
lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
#else
dwArgc = (DWORD) argc;
lpszArgv = argv;
#endif if( !faceless ) { //!! TCW MOD - no faceless, so give it a face.
SetupConsole(); //!! TCW MOD - make the console for debugging
_tprintf(TEXT("Debugging %s.\n"), m_lpDisplayName); SetConsoleCtrlHandler(ControlHandler, TRUE);
} //!! TCW MOD START - if Win95, register server
typedef DWORD (WINAPI *fp_RegServProc)(DWORD dwProcessId,DWORD dwType);
fp_RegServProc fncptr=NULL; if( faceless /*&& OsIsWin95()*/ ) {
WNDCLASS wndclass;
memset(&wndclass,0,sizeof(WNDCLASS));
wndclass.lpfnWndProc = _FacelessWndProc_;
wndclass.hInstance = HINSTANCE(::GetModuleHandle(0));
wndclass.lpszClassName = TEXT("RRL__FacelessWndProc_");
ATOM atom = ::RegisterClass(&wndclass);
HWND hwnd = ::CreateWindow(wndclass.lpszClassName,TEXT(""),0,0,0,0,0,0,0,wndclass.hInstance,0);
HMODULE hModule = ::GetModuleHandle(TEXT("kernel32.dll"));
// punch F1 on "RegisterServiceProcess" for what it does and when to use it.
fncptr=(fp_RegServProc)::GetProcAddress(hModule, "RegisterServiceProcess");
if (fncptr!=NULL)
(*fncptr)(0, RSP_SIMPLE_SERVICE);
}
//!! TCW MOD END - if Win95, register server
Run(dwArgc, lpszArgv);#ifdef UNICODE
::GlobalFree(HGLOBAL)lpszArgv);
#endif if (fncptr!=NULL) //!! TCW MOD - if it's there, remove it: our run is over
(*fncptr)(0, RSP_UNREGISTER_SERVICE); return TRUE;
}
void CNTService :: Pause() {
}
void CNTService :: Continue() {
}
void CNTService :: Shutdown() {
}
/////////////////////////////////////////////////////////////////////////////
// class CNTService -- default handlersvoid WINAPI CNTService :: ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
_ASSERTE( gpTheService != 0 ); // register our service control handler:
gpTheService->m_sshStatusHandle = RegisterServiceCtrlHandler(
gpTheService->m_lpServiceName,
CNTService::ServiceCtrl
); if( gpTheService->m_sshStatusHandle ) // report the status to the service control manager.
if( gpTheService->ReportStatus(SERVICE_START_PENDING) ){
gpTheService->Run( dwArgc, lpszArgv );} // try to report the stopped status to the service control manager.
if( gpTheService->m_sshStatusHandle )
gpTheService->ReportStatus(SERVICE_STOPPED);
}
void WINAPI CNTService :: ServiceCtrl(DWORD dwCtrlCode) {
_ASSERTE( gpTheService != 0 ); // Handle the requested control code.
switch( dwCtrlCode ) {
case SERVICE_CONTROL_STOP:
// Stop the service.
gpTheService->m_ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
gpTheService->Stop();
break; case SERVICE_CONTROL_PAUSE:
gpTheService->m_ssStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
gpTheService->Pause();
break; case SERVICE_CONTROL_CONTINUE:
gpTheService->m_ssStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
gpTheService->Continue();
break; case SERVICE_CONTROL_SHUTDOWN:
gpTheService->Shutdown();
break; case SERVICE_CONTROL_INTERROGATE:
// Update the service status.
gpTheService->ReportStatus(gpTheService->m_ssStatus.dwCurrentState);
break; default:
// invalid control code
break;
}}
BOOL WINAPI CNTService :: ControlHandler(DWORD dwCtrlType) {
_ASSERTE(gpTheService != 0);
switch( dwCtrlType ) {
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
_tprintf(TEXT("Stopping %s.\n"), gpTheService->m_lpDisplayName);
gpTheService->Stop();
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// class CNTService -- helpers//!! TCW MOD - added DWORD dwErrExit for error exit value. Defaults to zero
BOOL CNTService :: ReportStatus(
DWORD dwCurrentState,
DWORD dwWaitHint,
DWORD dwErrExit ) {
BOOL fResult = TRUE; if( !m_bDebug ) { // when debugging we don't report to the SCM
if( dwCurrentState == SERVICE_START_PENDING)
m_ssStatus.dwControlsAccepted = 0;
else
m_ssStatus.dwControlsAccepted = m_dwControlsAccepted; m_ssStatus.dwCurrentState = dwCurrentState;
m_ssStatus.dwWin32ExitCode = NO_ERROR;
m_ssStatus.dwWaitHint = dwWaitHint; //!! TCW MOD START - added code to support error exiting
m_ssStatus.dwServiceSpecificExitCode = dwErrExit;
if (dwErrExit!=0)
m_ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
//!! TCW MOD END - added code to support error exiting if( dwCurrentState == SERVICE_RUNNING ||
dwCurrentState == SERVICE_STOPPED )
m_ssStatus.dwCheckPoint = 0;
else
m_ssStatus.dwCheckPoint = ++m_dwCheckPoint; // Report the status of the service to the service control manager.
if (!(fResult = SetServiceStatus( m_sshStatusHandle, &m_ssStatus))) {
AddToMessageLog(TEXT("SetServiceStatus() failed"));
}
} return fResult;
}
void CNTService :: AddToMessageLog(LPTSTR lpszMsg, WORD wEventType, DWORD dwEventID) {
m_dwErr = GetLastError(); // use default message-IDs
if( dwEventID == DWORD(-1) ) {
switch( wEventType ) {
case EVENTLOG_ERROR_TYPE:
dwEventID = MSG_ERROR_1;
break;
case EVENTLOG_WARNING_TYPE:
dwEventID = MSG_WARNING_1;
break;
case EVENTLOG_INFORMATION_TYPE:
dwEventID = MSG_INFO_1;
break;
case EVENTLOG_AUDIT_SUCCESS:
dwEventID = MSG_INFO_1;
break;
case EVENTLOG_AUDIT_FAILURE:
dwEventID = MSG_INFO_1;
break;
default:
dwEventID = MSG_INFO_1;
break;
}
} // Use event logging to log the error.
HANDLE hEventSource = RegisterEventSource(0, m_lpServiceName); if( hEventSource != 0 ) {
LPCTSTR lpszMessage = lpszMsg; ReportEvent(
hEventSource, // handle of event source
wEventType, // event type
0, // event category
dwEventID, // event ID
m_pUserSID, // current user's SID
1, // strings in lpszStrings
0, // no bytes of raw data
&lpszMessage, // array of error strings
0 // no raw data
); ::DeregisterEventSource(hEventSource);
}
}
LPTSTR CNTService :: GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ) {
LPTSTR lpszTemp = 0; DWORD dwRet = ::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
0,
GetLastError(),
LANG_NEUTRAL,
(LPTSTR)&lpszTemp,
0,
0
); if( !dwRet || (dwSize < dwRet+14) )
lpszBuf[0] = TEXT('\0');
else {
lpszTemp[_tcsclen(lpszTemp)-2] = TEXT('\0'); //remove cr/nl characters
_tcscpy(lpszBuf, lpszTemp);
} if( lpszTemp )
LocalFree(HLOCAL(lpszTemp)); return lpszBuf;
}/////////////////////////////////////////////////////////////////////////////
// class CNTService -- implementationvoid CNTService :: RegisterApplicationLog( LPCTSTR lpszFileName, DWORD dwTypes ) {
TCHAR szKey[256];
_tcscpy(szKey, gszAppRegKey);
_tcscat(szKey, m_lpServiceName);
HKEY hKey = 0;
LONG lRet = ERROR_SUCCESS;
// Create a key for that application and insert values for
// "EventMessageFile" and "TypesSupported"
if( ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) == ERROR_SUCCESS ) {
lRet = ::RegSetValueEx(
hKey, // handle of key to set value for
TEXT("EventMessageFile"), // address of value to set
0, // reserved
REG_EXPAND_SZ, // flag for value type
(CONST BYTE*)lpszFileName, // address of value data
_tcslen(lpszFileName) + 1 // size of value data
); // Set the supported types flags.
lRet = ::RegSetValueEx(
hKey, // handle of key to set value for
TEXT("TypesSupported"), // address of value to set
0, // reserved
REG_DWORD, // flag for value type
(CONST BYTE*)&dwTypes, // address of value data
sizeof(DWORD) // size of value data
);
::RegCloseKey(hKey);
} // Add the service to the "Sources" value lRet = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE, // handle of open key
gszAppRegKey, // address of name of subkey to open
0, // reserved
KEY_ALL_ACCESS, // security access mask
&hKey // address of handle of open key
);
if( lRet == ERROR_SUCCESS ) {
DWORD dwSize; // retrieve the size of the needed value
lRet = ::RegQueryValueEx(
hKey, // handle of key to query
TEXT("Sources"),// address of name of value to query
0, // reserved
0, // address of buffer for value type
0, // address of data buffer
&dwSize // address of data buffer size
); if( lRet == ERROR_SUCCESS ) {
DWORD dwType;
DWORD dwNewSize = dwSize+_tcslen(m_lpServiceName)+1;
LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwNewSize)); lRet = ::RegQueryValueEx(
hKey, // handle of key to query
TEXT("Sources"),// address of name of value to query
0, // reserved
&dwType, // address of buffer for value type
Buffer, // address of data buffer
&dwSize // address of data buffer size
);
if( lRet == ERROR_SUCCESS ) {
_ASSERTE(dwType == REG_MULTI_SZ); // check whether this service is already a known source
register LPTSTR p = LPTSTR(Buffer);
for(; *p; p += _tcslen(p)+1 ) {
if( _tcscmp(p, m_lpServiceName) == 0 )
break;
}
if( ! * p ) {
// We're standing at the end of the stringarray
// and the service does still not exist in the "Sources".
// Now insert it at this point.
// Note that we have already enough memory allocated
// (see GlobalAlloc() above). We also don't need to append
// an additional '\0'. This is done in GlobalAlloc() above
// too.
_tcscpy(p, m_lpServiceName); // OK - now store the modified value back into the
// registry.
lRet = ::RegSetValueEx(
hKey, // handle of key to set value for
TEXT("Sources"),// address of value to set
0, // reserved
dwType, // flag for value type
Buffer, // address of value data
dwNewSize // size of value data
);
}
} ::GlobalFree(HGLOBAL(Buffer));
} ::RegCloseKey(hKey);
}
}
void CNTService :: DeregisterApplicationLog() {
TCHAR szKey[256];
_tcscpy(szKey, gszAppRegKey);
_tcscat(szKey, m_lpServiceName);
HKEY hKey = 0;
LONG lRet = ERROR_SUCCESS; lRet = ::RegDeleteKey(HKEY_LOCAL_MACHINE, szKey); // now we have to delete the application from the "Sources" value too.
lRet = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE, // handle of open key
gszAppRegKey, // address of name of subkey to open
0, // reserved
KEY_ALL_ACCESS, // security access mask
&hKey // address of handle of open key
);
if( lRet == ERROR_SUCCESS ) {
DWORD dwSize; // retrieve the size of the needed value
lRet = ::RegQueryValueEx(
hKey, // handle of key to query
TEXT("Sources"),// address of name of value to query
0, // reserved
0, // address of buffer for value type
0, // address of data buffer
&dwSize // address of data buffer size
); if( lRet == ERROR_SUCCESS ) {
DWORD dwType;
LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwSize));
LPBYTE NewBuffer = LPBYTE(::GlobalAlloc(GPTR, dwSize)); lRet = ::RegQueryValueEx(
hKey, // handle of key to query
TEXT("Sources"),// address of name of value to query
0, // reserved
&dwType, // address of buffer for value type
Buffer, // address of data buffer
&dwSize // address of data buffer size
);
if( lRet == ERROR_SUCCESS ) {
_ASSERTE(dwType == REG_MULTI_SZ); // check whether this service is already a known source
register LPTSTR p = LPTSTR(Buffer);
register LPTSTR pNew = LPTSTR(NewBuffer);
BOOL bNeedSave = FALSE; // assume the value is already correct
for(; *p; p += _tcslen(p)+1) {
// except ourself: copy the source string into the destination
if( _tcscmp(p, m_lpServiceName) != 0 ) {
_tcscpy(pNew, p);
pNew += _tcslen(pNew)+1;
} else {
bNeedSave = TRUE; // *this* application found
dwSize -= _tcslen(p)+1; // new size of value
}
}
if( bNeedSave ) {
// OK - now store the modified value back into the
// registry.
lRet = ::RegSetValueEx(
hKey, // handle of key to set value for
TEXT("Sources"),// address of value to set
0, // reserved
dwType, // flag for value type
NewBuffer, // address of value data
dwSize // size of value data
);
}
} ::GlobalFree(HGLOBAL(Buffer));
::GlobalFree(HGLOBAL(NewBuffer));
} ::RegCloseKey(hKey);
}
}//////////////////////////////////////////////////////////!! TCW MOD - function to create console for faceless apps if not already there
void CNTService::SetupConsole() {
if( !m_fConsoleReady ) {
AllocConsole(); // you only get 1 console. // lovely hack to get the standard io (printf, getc, etc) to the new console. Pretty much does what the
// C lib does for us, but when we want it, and inside of a Window'd app.
// The ugly look of this is due to the error checking (bad return values. Remove the if xxx checks if you like it that way.
DWORD astds[3]={STD_OUTPUT_HANDLE,STD_ERROR_HANDLE,STD_INPUT_HANDLE};
FILE *atrgs[3]={stdout,stderr,stdin};
for( register int i=0; i<3; i++ ) {
long hand=(long)GetStdHandle(astds[i]);
if( hand!=(long)INVALID_HANDLE_VALUE ) {
int osf=_open_osfhandle(hand,_O_TEXT);
if( osf!=-1 ) {
FILE *fp=_fdopen(osf,(astds[i]==STD_INPUT_HANDLE) ? "r" : "w");
if( fp!=NULL ) {
*(atrgs[i])=*fp;
setvbuf(fp,NULL,_IONBF,0);
}
}
}
}
m_fConsoleReady=TRUE;
}
}
#include "winsvc.h"void WINAPI ServiceMain(DWORD dwArgc,LPTSTR * lpszArgv);
void WINAPI Handler(DWORD Opcode);SERVICE_STATUS_HANDLE ssh;
SERVICE_STATUS ss;int main(int argc,char * argv[])
{
SERVICE_TABLE_ENTRY ste[2];
ste[0].lpServiceName="ServiceDemo";
ste[0].lpServiceProc=ServiceMain;
ste[1].lpServiceName=NULL;
ste[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(ste);
return 0;
}void WINAPI ServiceMain(DWORD dwArgc,LPTSTR * lpszArgv)
{
ssh=RegisterServiceCtrlHandler("ServiceDemo",Handler); ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_START_PENDING;
ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
//put your coed here
ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_RUNNING;
ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
//put your coed here
}void WINAPI Handler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_STOP:
//put your code here
ss.dwCurrentState=SERVICE_STOPPED;
ss.dwWin32ExitCode=0;
ss.dwCheckPoint=0;
ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
break;
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus(ssh,&ss);
break;
}
}
{
scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(scm!=NULL)
{
scv=::OpenService(scm,"ServiceDemo",SERVICE_ALL_ACCESS);
if (scv!=NULL)
{
::StartService(scv,0,NULL);
::CloseServiceHandle(scv);
}
::CloseServiceHandle(scm);
}
}OnCreate()
{
scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (scm!=NULL)
{
scv=::CreateService(scm,
"ServiceDemo",
"ServiceDemo",
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
"E:\\VC_DEMO\\ServDemo\\Debug\\ServDemo.exe",
NULL,NULL,NULL,NULL,NULL);
if (scv!=NULL)
{
::CloseServiceHandle(scv);
}
else
{
::CloseServiceHandle(scm);
}
}
}
OnDelete()
{
scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (scm!=NULL)
{
scv=::OpenService(scm,"ServiceDemo",SERVICE_ALL_ACCESS);
if (scv!=NULL)
{
::QueryServiceStatus(scv,&ss);
if (ss.dwCurrentState==SERVICE_RUNNING)
{
::ControlService(scv,SERVICE_CONTROL_STOP,&ss);
}
::DeleteService(scv);
::CloseServiceHandle(scv);
}
::CloseServiceHandle(scm);
}
}OnStop()
{
scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(scm!=NULL)
{
scv=::OpenService(scm,"ServiceDemo",SERVICE_STOP|SERVICE_QUERY_STATUS);
if (scv!=NULL)
{
::QueryServiceStatus(scv,&ss);
if (ss.dwCurrentState==SERVICE_RUNNING)
{
::ControlService(scv,SERVICE_CONTROL_STOP,&ss);
}
::CloseServiceHandle(scv);
}
::CloseServiceHandle(scm);
}
}