www.pudn.com > VC_telnet.rar > NTService.cpp
#include///////////////////////////////////////////////////////////////////////////// // Copyright (C) 1997 by Joerg Koenig and the ADG mbH, Mannheim, Germany // All rights reserved // // Distribute freely, except: don't remove my name from the source or // documentation (don't take credit for my work), mark your changes (don't // get me blamed for your possible bugs), don't alter or remove this // notice. // No warrantee of any kind, express or implied, is included with this // software; use at your own risk, responsibility for damages (if any) to // anyone resulting from the use of this software rests entirely with the // user. // // Send bug reports, bug fixes, enhancements, requests, flames, etc., and // I'll try to keep a version up to date. I can be reached as follows: // J.Koenig@adg.de (company site) // Joerg.Koenig@rhein-neckar.de (private site) ///////////////////////////////////////////////////////////////////////////// // // MODIFIED BY TODD C. WILSON FOR THE ROAD RUNNER NT LOGIN SERVICE. // HOWEVER, THESE MODIFICATIONS ARE BROADER IN SCOPE AND USAGE AND CAN BE USED // IN OTHER PROJECTS WITH NO CHANGES. // MODIFIED LINES FLAGGED/BRACKETED BY "//!! TCW MOD" // ///////////////////////////////////////////////////////////////////////////// // last revised: $Date: 11.05.98 21:09 $, $Revision: 3 $ ///////////////////////////////////////////////////////////////////////////// // Acknoledgements: // o Thanks to Victor Vogelpoel (VictorV@Telic.nl) for his bug-fixes // and enhancements. // o Thanks to Todd C. Wilson (todd@mediatec.com) for the // "service" on Win95 // // Changes: // 01/21/99 // o Bug fixed in "DeregisterApplicationLog()" // thanks to Grahame Willis (grahamew@questsoftware.com.au): // // 04/30/98 // o Added two more switches to handle command line arguments: // -e will force a running service to stop (corresponding // method in this class: virtual BOOL EndService();) and // -s will force the service to start (method: // virtual BOOL StartupService()) // // 02/05/98 // o Added the methods "RegisterApplicationLog()" and // "DeregisterApplicationLog()" (both virtual). The first one will be // called from "InstallService()" and creates some registry-entries // for a better event-log. The second one removes these entries when // the service will uninstall (see "RemoveService()") // o The service now obtains the security identifier of the current user // and uses this SID for event-logging. // o The memory allocated by "CommandLineToArgvW()" will now release // (UNICODE version only) // o The service now uses a simple message catalogue for a nicer // event logging #include #include #include #include #include //!! TCW MOD #include //!! 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 #endif BOOL CNTService :: m_bInstance = FALSE; static CNTService * gpTheService = 0; // the one and only instance CNTService * 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/destruction CNTService :: 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 (VictorV@Telic.nl). // 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 handlers void 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 -- implementation void 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; } }