/* * Win32Service.java * * Created on 12. September 2007, 12:05 * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package jnacontrib.win32; import jnacontrib.jna.*; import com.sun.jna.Pointer; /** * Baseclass for a Win32 service. */ public abstract class Win32Service { protected String serviceName; private ServiceMain serviceMain; private ServiceControl serviceControl; private Pointer serviceStatusHandle; private Object waitObject = new Object(); /** * Creates a new instance of Win32Service. * * @param serviceName internal name of the service */ public Win32Service(String serviceName) { this.serviceName = serviceName; } /** * Install the service. * * @param displayName visible name * @param description description * @param dependencies array of other services to depend on or null * @param account service account or null for LocalSystem * @param password password for service account or null * @throws java.lang.Exception * @return true on success */ public boolean install(String displayName, String description, String[] dependencies, String account, String password) { return(install(displayName, description, dependencies, account, password, "java.exe -cp \"" + System.getProperty("java.class.path") + "\" -Xrs " + this.getClass().getName())); } /** * Install the service. * * @return true on success * @param displayName visible name * @param description description * @param dependencies array of other services to depend on or null * @param account service account or null for LocalSystem * @param password password for service account or null * @param command command line to start the service * @throws java.lang.Exception */ public boolean install(String displayName, String description, String[] dependencies, String account, String password, String command) { Advapi32 advapi32; Advapi32.SERVICE_DESCRIPTION desc; Pointer serviceManager, service; boolean success = false; String dep = ""; if(dependencies != null) { for(String s : dependencies) { dep += s + "\0"; } } dep += "\0"; desc = new Advapi32.SERVICE_DESCRIPTION(); desc.lpDescription = description; advapi32 = Advapi32.INSTANCE; serviceManager = openServiceControlManager(null, WINSVC.SC_MANAGER_ALL_ACCESS); if(serviceManager != null) { service = advapi32.CreateService(serviceManager, serviceName, displayName, WINSVC.SERVICE_ALL_ACCESS, WINSVC.SERVICE_WIN32_OWN_PROCESS, WINSVC.SERVICE_DEMAND_START, WINSVC.SERVICE_ERROR_NORMAL, command, null, null, dep, account, password); if(service != null) { success = advapi32.ChangeServiceConfig2(service, WINSVC.SERVICE_CONFIG_DESCRIPTION, desc); advapi32.CloseServiceHandle(service); } advapi32.CloseServiceHandle(serviceManager); } return(success); } /** * Uninstall the service. * * @throws java.lang.Exception * @return true on success */ public boolean uninstall() { Advapi32 advapi32; Pointer serviceManager, service; boolean success = false; advapi32 = Advapi32.INSTANCE; serviceManager = openServiceControlManager(null, WINSVC.SC_MANAGER_ALL_ACCESS); if(serviceManager != null) { service = advapi32.OpenService(serviceManager, serviceName, WINSVC.SERVICE_ALL_ACCESS); if(service != null) { success = advapi32.DeleteService(service); advapi32.CloseServiceHandle(service); } advapi32.CloseServiceHandle(serviceManager); } return(success); } /** * Ask the ServiceControlManager to start the service. * @return true on success */ public boolean start() { Advapi32 advapi32; Pointer serviceManager, service; boolean success = false; advapi32 = Advapi32.INSTANCE; serviceManager = openServiceControlManager(null, WINNT.GENERIC_EXECUTE); if(serviceManager != null) { service = advapi32.OpenService(serviceManager, serviceName, WINNT.GENERIC_EXECUTE); if(service != null) { success = advapi32.StartService(service, 0, null); advapi32.CloseServiceHandle(service); } advapi32.CloseServiceHandle(serviceManager); } return(success); } /** * Ask the ServiceControlManager to stop the service. * @return true on success */ public boolean stop() throws Exception { Advapi32 advapi32; Pointer serviceManager, service; Advapi32.SERVICE_STATUS serviceStatus; boolean success = false; advapi32 = Advapi32.INSTANCE; serviceManager = openServiceControlManager(null, WINNT.GENERIC_EXECUTE); if(serviceManager != null) { service = advapi32.OpenService(serviceManager, serviceName, WINNT.GENERIC_EXECUTE); if(service != null) { serviceStatus = new Advapi32.SERVICE_STATUS(); success = advapi32.ControlService(service, WINSVC.SERVICE_CONTROL_STOP, serviceStatus); advapi32.CloseServiceHandle(service); } advapi32.CloseServiceHandle(serviceManager); } return(success); } /** * Initialize the service, connect to the ServiceControlManager. */ public void init() { Advapi32 advapi32; Advapi32.SERVICE_TABLE_ENTRY[] entries = new Advapi32.SERVICE_TABLE_ENTRY[2]; Advapi32.SERVICE_TABLE_ENTRY entry; serviceMain = new ServiceMain(); advapi32 = Advapi32.INSTANCE; entry = new Advapi32.SERVICE_TABLE_ENTRY(); entry.lpServiceName = serviceName; entry.lpServiceProc = serviceMain; advapi32.StartServiceCtrlDispatcher(entry.toArray(2)); } /** * Get a handle to the ServiceControlManager. * * @param machine name of the machine or null for localhost * @param access access flags * @return handle to ServiceControlManager or null when failed */ private Pointer openServiceControlManager(String machine, int access) { Pointer handle = null; Advapi32 advapi32; advapi32 = Advapi32.INSTANCE; handle = advapi32.OpenSCManager(machine, null, access); return(handle); } /** * Report service status to the ServiceControlManager. * * @param status status * @param win32ExitCode exit code * @param waitHint time to wait */ private void reportStatus(int status, int win32ExitCode, int waitHint) { Advapi32 advapi32; Advapi32.SERVICE_STATUS serviceStatus; advapi32 = Advapi32.INSTANCE; serviceStatus = new Advapi32.SERVICE_STATUS(); serviceStatus.dwServiceType = WINNT.SERVICE_WIN32_OWN_PROCESS; serviceStatus.dwControlsAccepted = WINSVC.SERVICE_ACCEPT_STOP | WINSVC.SERVICE_ACCEPT_SHUTDOWN; serviceStatus.dwWin32ExitCode = win32ExitCode; serviceStatus.dwWaitHint = waitHint; serviceStatus.dwCurrentState = status; advapi32.SetServiceStatus(serviceStatusHandle, serviceStatus); } /** * Called when service is starting. */ public abstract void onStart(); /* * Called when service should stop. */ public abstract void onStop(); /** * Implementation of the service main function. */ private class ServiceMain implements Advapi32.SERVICE_MAIN_FUNCTION { /** * Called when the service is starting. * * @param dwArgc number of arguments * @param lpszArgv pointer to arguments */ public void callback(int dwArgc, Pointer lpszArgv) { Advapi32 advapi32; advapi32 = Advapi32.INSTANCE; serviceControl = new ServiceControl(); serviceStatusHandle = advapi32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null); reportStatus(WINSVC.SERVICE_START_PENDING, WINERROR.NO_ERROR, 3000); reportStatus(WINSVC.SERVICE_RUNNING, WINERROR.NO_ERROR, 0); onStart(); try { synchronized(waitObject) { waitObject.wait(); } } catch (InterruptedException ex) { } reportStatus(WINSVC.SERVICE_STOPPED, WINERROR.NO_ERROR, 0); // Avoid returning from ServiceMain, which will cause a crash // See http://support.microsoft.com/kb/201349, which recommends // having init() wait for this thread. // Waiting on this thread in init() won't fix the crash, though. //System.exit(0); } } /** * Implementation of the service control function. */ private class ServiceControl implements Advapi32.HandlerEx { /** * Called when the service get a control code. * * @param dwControl * @param dwEventType * @param lpEventData * @param lpContext */ public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) { switch(dwControl) { case WINSVC.SERVICE_CONTROL_STOP: case WINSVC.SERVICE_CONTROL_SHUTDOWN: reportStatus(WINSVC.SERVICE_STOP_PENDING, WINERROR.NO_ERROR, 5000); onStop(); synchronized(waitObject) { waitObject.notifyAll(); } } return WINERROR.NO_ERROR; } } }