From e829c3bf72c2487992034fa7d93aae43c6e5069e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sat, 12 Aug 2017 16:25:03 +0200 Subject: [PATCH] Move Windows Service functions from ntservice project to core The ntservice poject contained additional definitions for windows service related functionality. Unittests are added in the form of a sample service implementation, that is installed/uninstalled and invoked. com.sun.jna.platform.win32.Advapi32: - StartServiceCtrlDispatcher - RegisterServiceCtrlHandler - RegisterServiceCtrlHandlerEx - SetServiceStatus - CreateService - DeleteService com.sun.jna.platform.win32.Winsvc: - Handler - HandlerEx - SERVICE_MAIN_FUNCTION - SERVICE_TABLE_ENTRY - SERVICE_DESCRIPTION - SERVICE_STATUS_HANDLE Closes: #821 Closes: #258 --- CHANGES.md | 1 + .../src/jnacontrib/jna/Advapi32.java | 659 ------------------ .../src/jnacontrib/win32/Win32Service.java | 126 ++-- .../com/sun/jna/platform/win32/Advapi32.java | 335 +++++++++ .../sun/jna/platform/win32/W32Service.java | 5 - .../com/sun/jna/platform/win32/Winsvc.java | 381 +++++++++- .../jna/platform/win32/W32ServiceTest.java | 236 ++++--- .../jna/platform/win32/Win32ServiceDemo.java | 367 ++++++++++ 8 files changed, 1250 insertions(+), 860 deletions(-) delete mode 100644 contrib/ntservice/src/jnacontrib/jna/Advapi32.java create mode 100644 contrib/platform/test/com/sun/jna/platform/win32/Win32ServiceDemo.java diff --git a/CHANGES.md b/CHANGES.md index 737f172f0d..799dc043fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ Features * [#816](https://github.com/java-native-access/jna/pull/816): Support `boolean[]` in direct mapping - [@ncruces](https://github.com/ncruces). * [#827](https://github.com/java-native-access/jna/pull/827): Add support for linux-mips64el - [@all7](https://github.com/all7). * [#845](https://github.com/java-native-access/jna/issues/845): Add support for linux-s390x - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#821](https://github.com/java-native-access/jna/issues/821): Move windows service related functions and structures in ntservice sample project to `com.sun.jna.platform.win32.Advapi32`: `StartServiceCtrlDispatcher`, `RegisterServiceCtrlHandler`, `RegisterServiceCtrlHandlerEx`, `SetServiceStatus`, `CreateService`, `DeleteService` and `com.sun.jna.platform.win32.Winsvc`: `Handler`, `HandlerEx`, `SERVICE_MAIN_FUNCTION`, `SERVICE_TABLE_ENTRY`, `SERVICE_DESCRIPTION`, `SERVICE_STATUS_HANDLE` - [@matthiasblaesing](https://github.com/matthiasblaesing). Bug Fixes --------- diff --git a/contrib/ntservice/src/jnacontrib/jna/Advapi32.java b/contrib/ntservice/src/jnacontrib/jna/Advapi32.java deleted file mode 100644 index 5b314449ae..0000000000 --- a/contrib/ntservice/src/jnacontrib/jna/Advapi32.java +++ /dev/null @@ -1,659 +0,0 @@ -/* - * The contents of this file is dual-licensed under 2 - * alternative Open Source/Free licenses: LGPL 2.1 or later and - * Apache License 2.0. (starting with JNA version 4.0.0). - * - * You can freely decide which license you want to apply to - * the project. - * - * You may obtain a copy of the LGPL License at: - * - * http://www.gnu.org/licenses/licenses.html - * - * A copy is also included in the downloadable source code package - * containing JNA, in file "LGPL2.1". - * - * You may obtain a copy of the Apache License at: - * - * http://www.apache.org/licenses/ - * - * A copy is also included in the downloadable source code package - * containing JNA, in file "AL2.0". - */ - -package jnacontrib.jna; - -import java.util.List; - -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.Structure; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.ptr.IntByReference; -import com.sun.jna.win32.W32APIOptions; -import com.sun.jna.platform.win32.Winsvc.ChangeServiceConfig2Info; -import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; -import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS; -import com.sun.jna.win32.W32APITypeMapper; - -/** - * - * @author TB - */ -public interface Advapi32 extends com.sun.jna.platform.win32.Advapi32 { - - Advapi32 INSTANCE = Native.loadLibrary("Advapi32", Advapi32.class, W32APIOptions.DEFAULT_OPTIONS); - - /** - * Connects the main thread of a service process to the service control - * manager, which causes the thread to be the service control dispatcher - * thread for the calling process. - * - * @param lpServiceTable A pointer to an array of SERVICE_TABLE_ENTRY - * structures containing one entry for each service - * that can execute in the calling process. The - * members of the last entry in the table must have - * NULL values to designate the end of the table. - * - * @return true if function succeeds. To get extended error information, call - * GetLastError. Possible error codes: - * - * - * - * - * - *
Return codeDescription
ERROR_FAILED_SERVICE_CONTROLLER_CONNECTThis error is returned if the program is being run as a console application rather than as a service. If the program will be run as a console application for debugging purposes, structure it such that service-specific code is not called when this error is returned.
ERROR_INVALID_DATAThe specified dispatch table contains entries that are not in the proper format.
ERROR_SERVICE_ALREADY_RUNNINGThe process has already called StartServiceCtrlDispatcher. Each process can call StartServiceCtrlDispatcher only one time.
- */ - public boolean StartServiceCtrlDispatcher(SERVICE_TABLE_ENTRY[] lpServiceTable); - - /** - * Registers a function to handle service control requests. - * - *

This function has been superseded by the RegisterServiceCtrlHandlerEx - * function. A service can use either function, but the new function - * supports user-defined context data, and the new handler function supports - * additional extended control codes.

- * - * @param lpServiceName The name of the service run by the calling thread. - * This is the service name that the service control - * program specified in the CreateService function when - * creating the service. - * - *

If the service type is SERVICE_WIN32_OWN_PROCESS, - * the function does not verify that the specified name - * is valid, because there is only one registered - * service in the process.

- * - * @param lpHandlerProc A pointer to the handler function to be registered. - * For more information, see {@link Handler}. - * - * @return A service status handle, NULL on error. Call GetLastError to - * get extended error condition. Possible error codes: - * - * - * - * - *
Return codeDescription
ERROR_NOT_ENOUGH_MEMORYNot enough memory is available to convert an ANSI string parameter to Unicode. This error does not occur for Unicode string parameters.
ERROR_SERVICE_NOT_IN_EXEThe service entry was specified incorrectly when the process called the {@link #StartServiceCtrlDispatcher} function.
- */ - public SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(String lpServiceName, - Handler lpHandlerProc); - - /** - * Registers a function to handle extended service control requests. - * - * @param lpServiceName The name of the service run by the calling thread. - * This is the service name that the service control - * program specified in the CreateService function when - * creating the service. - * @param lpHandlerProc The handler function to be registered. - * For more information, see HandlerEx. - * @param lpContext Any user-defined data. This parameter, which is - * passed to the handler function, can help identify - * the service when multiple services share a process. - * - * @return A service status handle on success, NULL on error. Call GetLastError - * to get extended information. Possible error codes: - * - * - * - * - *
Return codeDescription
ERROR_NOT_ENOUGH_MEMORYNot enough memory is available to convert an ANSI string parameter to Unicode. This error does not occur for Unicode string parameters.
ERROR_SERVICE_NOT_IN_EXEThe service entry was specified incorrectly when the process called the {@link #StartServiceCtrlDispatcher} function.
- */ - public SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerEx(String lpServiceName, - HandlerEx lpHandlerProc, Pointer lpContext); - - /** - * Updates the service control manager's status information for the calling - * service. - * - * - * @param hServiceStatus A handle to the status information structure for - * the current service. This handle is returned by - * the RegisterServiceCtrlHandlerEx function. - * @param lpServiceStatus A pointer to the SERVICE_STATUS structure the - * contains the latest status information for the - * calling service. - * - * @return true if function succeeds. To get extended error information, call - * GetLastError. Possible error codes: - * - * - * - * - *
Return codeDescription
ERROR_INVALID_DATAThe specified service status structure is invalid.
ERROR_INVALID_HANDLEThe specified handle is invalid.
- */ - public boolean SetServiceStatus(SERVICE_STATUS_HANDLE hServiceStatus, - SERVICE_STATUS lpServiceStatus); - - /** - * Creates a service object and adds it to the specified service control - * manager database. - * - * @param hSCManager [in] A handle to the service control manager - * database. This handle is returned by the - * OpenSCManager function and must have the - * SC_MANAGER_CREATE_SERVICE access right. For - * more information, see Service Security and - * Access Rights. - * @param lpServiceName [in] The name of the service to install. The - * maximum string length is 256 characters. The - * service control manager database preserves the - * case of the characters, but service name - * comparisons are always case insensitive. - * Forward-slash (/) and backslash (\) are not - * valid service name characters. - * @param lpDisplayName [in, optional] The display name to be used by - * user interface programs to identify the - * service. This string has a maximum length of - * 256 characters. The name is case-preserved in - * the service control manager. Display name - * comparisons are always case-insensitive. - * @param dwDesiredAccess [in] The access to the service. Before granting - * the requested access, the system checks the - * access token of the calling process. For a list - * of values, see Service Security and Access - * Rights. - * @param dwServiceType [in] The service type. This parameter can be - * one of the following values. - * - * - * - * - * - * - * - * - *
ValueMeaning
SERVICE_ADAPTER
0x00000004
Reserved.
SERVICE_FILE_SYSTEM_DRIVER
0x00000002
File system driver service.
SERVICE_KERNEL_DRIVER
0x00000001
Driver service.
SERVICE_RECOGNIZER_DRIVER
0x00000008
Reserved.
SERVICE_WIN32_OWN_PROCESS
0x00000010
Service that runs in its own process.
SERVICE_WIN32_SHARE_PROCESS
0x00000020
Service that shares a process with one or more other services. For more information, see Service Programs.
- * - *

If you specify either SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS, and the service is running in the context of the LocalSystem account, you can also specify the following value.

- * - * - * - * - *
ValueMeaning
SERVICE_INTERACTIVE_PROCESS
0x00000100
The service can interact with the desktop.
- * - * @param dwStartType [in] The service start options. This parameter - * can be one of the following values. - * - * - * - * - * - * - * - * - *
ValueMeaning
SERVICE_AUTO_START
0x00000002
A service started automatically by the service control manager during system startup.
SERVICE_BOOT_START
0x00000000
A device driver started by the system loader. This value is valid only for driver services.
SERVICE_DEMAND_START
0x00000003
A service started by the service control manager when a process calls the StartService function.
SERVICE_DISABLED
0x00000004
A service that cannot be started. Attempts to start the service result in the error code ERROR_SERVICE_DISABLED.
SERVICE_SYSTEM_START
0x00000001
A device driver started by the IoInitSystem function. This value is valid only for driver services.
- * - * @param dwErrorControl [in] The severity of the error, and action - * taken, if this service fails to start. This - * parameter can be one of the following values. - * - * - * - * - * - * - * - *
ValueMeaning
SERVICE_ERROR_CRITICAL
0x00000003
The startup program logs the error in the event log, if possible. If the last-known-good configuration is being started, the startup operation fails. Otherwise, the system is restarted with the last-known good configuration.
SERVICE_ERROR_IGNORE
0x00000000
The startup program ignores the error and continues the startup operation.
SERVICE_ERROR_NORMAL
0x00000001
The startup program logs the error in the event log but continues the startup operation.
SERVICE_ERROR_SEVERE
0x00000002
The startup program logs the error in the event log. If the last-known-good configuration is being started, the startup operation continues. Otherwise, the system is restarted with the last-known-good configuration.
- * - * @param lpBinaryPathName [in, optional] The fully qualified path to the - * service binary file. If the path contains a - * space, it must be quoted so that it is - * correctly interpreted. For example, "d:\\my - * share\\myservice.exe" should be specified as - * "\"d:\\my share\\myservice.exe\"". - * - *

The path can also include arguments for an - * auto-start service. For example, - * "d:\\myshare\\myservice.exe arg1 arg2". These - * passed to the service entry point (typically - * the main function).

- * - *

If you specify a path on another computer, - * the share must be accessible by the computer - * account of the local computer because this is - * the security context used in the remote call. - * However, this requirement allows any potential - * vulnerabilities in the remote computer to - * affect the local computer. Therefore, it is - * best to use a local file.

- * - * @param lpLoadOrderGroup [in, optional] The names of the load ordering - * group of which this service is a member. - * Specify NULL or an empty string if the service - * does not belong to a group. - * - *

The startup program uses load ordering - * groups to load groups of services in a - * specified order with respect to the other - * groups. The list of load ordering groups is - * contained in the following registry value:

- * - *

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder

- * @param lpdwTagId [out, optional] A pointer to a variable that - * receives a tag value that is unique in the - * group specified in the lpLoadOrderGroup - * parameter. Specify NULL if you are not changing - * the existing tag. - * - *

You can use a tag for ordering service - * startup within a load ordering group by - * specifying a tag order vector in the following - * registry value:

- * - *

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList

- * - *

Tags are only evaluated for driver services - * that have SERVICE_BOOT_START or - * SERVICE_SYSTEM_START start types.

- * @param lpDependencies [in, optional] A pointer to a double - * null-terminated array of null-separated names - * of services or load ordering groups that the - * system must start before this service. Specify - * NULL or an empty string if the service has no - * dependencies. Dependency on a group means that - * this service can run if at least one member of - * the group is running after an attempt to start - * all members of the group. - * - *

You must prefix group names with - * SC_GROUP_IDENTIFIER so that they can be - * distinguished from a service name, because - * services and service groups share the same name - * space.

- * @param lpServiceStartName [in, optional] The name of the account under - * which the service should run. If the service - * type is SERVICE_WIN32_OWN_PROCESS, use an - * account name in the form DomainName\UserName. - * The service process will be logged on as this - * user. If the account belongs to the built-in - * domain, you can specify .\UserName. - * - *

If this parameter is NULL, CreateService - * uses the LocalSystem account. If the service - * type specifies SERVICE_INTERACTIVE_PROCESS, the - * service must run in the LocalSystem account.

- * - *

If this parameter is NT AUTHORITY\LocalService, - * CreateService uses the LocalService account. If - * the parameter is NT AUTHORITY\NetworkService, - * CreateService uses the NetworkService account.

- * - *

A shared process can run as any user.

- * - *

If the service type is SERVICE_KERNEL_DRIVER - * or SERVICE_FILE_SYSTEM_DRIVER, the name is the - * driver object name that the system uses to load - * the device driver. Specify NULL if the driver - * is to use a default object name created by the - * I/O system.

- * - *

A service can be configured to use a managed - * account or a virtual account. If the service is - * configured to use a managed service account, - * the name is the managed service account name. - * If the service is configured to use a virtual - * account, specify the name as NT - * SERVICE\ServiceName. For more information about - * managed service accounts and virtual accounts, - * see the Service Accounts Step-by-Step Guide. - * - *

Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: - * Managed service accounts and virtual accounts are not supported until - * Windows 7 and Windows Server 2008 R2.

- * @param lpPassword [in, optional] The password to the account name - * specified by the lpServiceStartName parameter. - * Specify an empty string if the account has no - * password or if the service runs in the - * LocalService, NetworkService, or LocalSystem - * account. For more information, see Service - * Record List. - * - *

If the account name specified by the - * lpServiceStartName parameter is the name of a - * managed service account or virtual account - * name, the lpPassword parameter must be NULL.

- * - *

Passwords are ignored for driver services.

- * - * @return SC_HANDLE on success, NULL on error. Call GetLastError to - * get extended error condition. Possible error codes: - * - * - * - * - * - * - * - * - * - * - * - * - *
Return codeDescription
ERROR_ACCESS_DENIEDThe handle to the SCM database does not have the SC_MANAGER_CREATE_SERVICE access right.
ERROR_CIRCULAR_DEPENDENCYA circular service dependency was specified.
ERROR_DUPLICATE_SERVICE_NAMEThe display name already exists in the service control manager database either as a service name or as another display name.
ERROR_INVALID_HANDLEThe handle to the specified service control manager database is invalid.
ERROR_INVALID_NAMEThe specified service name is invalid.
ERROR_INVALID_PARAMETERA parameter that was specified is invalid.
ERROR_INVALID_SERVICE_ACCOUNTThe user account name specified in the lpServiceStartName parameter does not exist.
ERROR_SERVICE_EXISTSThe specified service already exists in this database.
ERROR_SERVICE_MARKED_FOR_DELETEThe specified service already exists in this database and has been marked for deletion.
- */ - public SC_HANDLE CreateService(SC_HANDLE hSCManager, String lpServiceName, - String lpDisplayName, int dwDesiredAccess, int dwServiceType, - int dwStartType, int dwErrorControl, String lpBinaryPathName, - String lpLoadOrderGroup, IntByReference lpdwTagId, - String lpDependencies, String lpServiceStartName, String lpPassword); - - /** - * Marks the specified service for deletion from the service control manager database. - * - * @param hService [in] A handle to the service. This handle is returned by - * the OpenService or CreateService function, and it must - * have the DELETE access right. - * - * @return true if function succeeds. To get extended error information, call - * GetLastError. Possible error codes: - * - * - * - * - * - * - *
Return codeDescription
ERROR_ACCESS_DENIEDThe handle does not have the DELETE access right.
ERROR_INVALID_HANDLEThe specified handle is invalid.
ERROR_SERVICE_MARKED_FOR_DELETEThe specified service has already been marked for deletion.
- */ - public boolean DeleteService(SC_HANDLE hService); - - /** - * The entry point for a service. - */ - interface SERVICE_MAIN_FUNCTION extends StdCallCallback { - - /** - * - * @param dwArgc [in] The number of arguments in the lpszArgv array. - * @param lpszArgv [in] The null-terminated argument strings passed to - * the service by the call to the StartService function - * that started the service. If there are no arguments, - * this parameter can be NULL. Otherwise, the first - * argument (lpszArgv[0]) is the name of the service, - * followed by any additional arguments (lpszArgv[1] - * through lpszArgv[dwArgc-1]). - * - *

If the user starts a manual service using the - * Services snap-in from the Control Panel, the strings - * for the lpszArgv parameter come from the properties - * dialog box for the service (from the Services snap-in, - * right-click the service entry, click Properties, and - * enter the parameters in Start parameters.) - */ - public void callback(int dwArgc, Pointer lpszArgv); - } - - /** - * An application-defined callback function used with the - * RegisterServiceCtrlHandler function. A service program can use it as the - * control handler function of a particular service. - * - *

- * This function has been superseded by the {@link HandlerEx} control handler - * function used with the {@link #RegisterServiceCtrlHandlerEx} function. A service - * can use either control handler, but the new control handler supports - * user-defined context data and additional extended control codes.

- */ - interface Handler extends StdCallCallback { - - /** - * @param fdwControl [in] The control code. This parameter can be one of - * the following values. - * - * - * - * - * - * - * - * - * - * - * - * - *
Control codeMeaning
SERVICE_CONTROL_CONTINUE
0x00000003
Notifies a paused service that it should resume.
SERVICE_CONTROL_INTERROGATE
0x00000004
Notifies a service that it should report its current status information to the service control manager.
The handler should simply return NO_ERROR; the SCM is aware of the current state of the service.
SERVICE_CONTROL_NETBINDADD
0x00000007
Notifies a network service that there is a new component for binding. The service should bind to the new component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDDISABLE
0x0000000A
Notifies a network service that one of its bindings has been disabled. The service should reread its binding information and remove the binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDENABLE
0x00000009
Notifies a network service that a disabled binding has been enabled. The service should reread its binding information and add the new binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDREMOVE
0x00000008
Notifies a network service that a component for binding has been removed. The service should reread its binding information and unbind from the removed component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_PARAMCHANGE
0x00000006
Notifies a service that its startup parameters have changed. The service should reread its startup parameters.
SERVICE_CONTROL_PAUSE
0x00000002
Notifies a service that it should pause.
SERVICE_CONTROL_SHUTDOWN
0x00000005
Notifies a service that the system is shutting down so the service can perform cleanup tasks.
If a service accepts this control code, it must stop after it performs its cleanup tasks and return NO_ERROR. After the SCM sends this control code, it will not send other control codes to the service.
SERVICE_CONTROL_STOP
0x00000001
Notifies a service that it should stop.
If a service accepts this control code, it must stop upon receipt and return NO_ERROR. After the SCM sends this control code, it does not send other control codes.
Windows XP: If the service returns NO_ERROR and continues to run, it continues to receive control codes. This behavior changed starting with Windows Server 2003 and Windows XP with SP2.
- * - *

This parameter can also be a user-defined control code, as described in the following table.

- * - * - * - * - *
Control codeMeaning
Range 128 to 255.The service defines the action associated with the control code.
- */ - public void callback(int fdwControl); - } - - /** - * An application-defined callback function used with the - * RegisterServiceCtrlHandlerEx function. A service program can use it as - * the control handler function of a particular service. - * - *

This function supersedes the Handler control handler function used - * with the {@link RegisterServiceCtrlHandler} function. A service can use either - * control handler, but the new control handler supports user-defined - * context data and additional extended control codes.

- */ - interface HandlerEx extends StdCallCallback { - - /** - * @param dwControl [in] The control code. This parameter can be one of - * the following values. - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Control codeMeaning
SERVICE_CONTROL_CONTINUE
0x00000003
Notifies a paused service that it should resume.
SERVICE_CONTROL_INTERROGATE
0x00000004
Notifies a service that it should report its current status information to the service control manager.
The handler should simply return NO_ERROR; the SCM is aware of the current state of the service.
SERVICE_CONTROL_NETBINDADD
0x00000007
Notifies a network service that there is a new component for binding. The service should bind to the new component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDDISABLE
0x0000000A
Notifies a network service that one of its bindings has been disabled. The service should reread its binding information and remove the binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDENABLE
0x00000009
Notifies a network service that a disabled binding has been enabled. The service should reread its binding information and add the new binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDREMOVE
0x00000008
Notifies a network service that a component for binding has been removed. The service should reread its binding information and unbind from the removed component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_PARAMCHANGE
0x00000006
Notifies a service that its startup parameters have changed. The service should reread its startup parameters.
SERVICE_CONTROL_PAUSE
0x00000002
Notifies a service that it should pause.
SERVICE_CONTROL_PRESHUTDOWN
0x0000000F
Notifies a service that the system will be shutting down. Services that need additional time to perform cleanup tasks beyond the tight time restriction at system shutdown can use this notification. The service control manager sends this notification to applications that have registered for it before sending a SERVICE_CONTROL_SHUTDOWN notification to applications that have registered for that notification.
A service that handles this notification blocks system shutdown until the service stops or the preshutdown time-out interval specified through SERVICE_PRESHUTDOWN_INFO expires. Because this affects the user experience, services should use this feature only if it is absolutely necessary to avoid data loss or significant recovery time at the next system start.
Windows Server 2003 and Windows XP: This value is not supported.
SERVICE_CONTROL_SHUTDOWN
0x00000005
Notifies a service that the system is shutting down so the service can perform cleanup tasks.
If a service accepts this control code, it must stop after it performs its cleanup tasks and return NO_ERROR. After the SCM sends this control code, it will not send other control codes to the service.
SERVICE_CONTROL_STOP
0x00000001
Notifies a service that it should stop.
If a service accepts this control code, it must stop upon receipt and return NO_ERROR. After the SCM sends this control code, it does not send other control codes.
Windows XP: If the service returns NO_ERROR and continues to run, it continues to receive control codes. This behavior changed starting with Windows Server 2003 and Windows XP with SP2.
- * - *

This parameter can also be one of the following extended control codes. Note that these control codes are not supported by the {@link Handler} function.

- * - * - * - * - * - * - * - * - * - * - *
Control codeMeaning
SERVICE_CONTROL_DEVICEEVENT
0x0000000B
Notifies a service of device events. (The service must have registered to receive these notifications using the RegisterDeviceNotification function.) The dwEventType and lpEventData parameters contain additional information.
SERVICE_CONTROL_HARDWAREPROFILECHANGE
0x0000000C
Notifies a service that the computer's hardware profile has changed. The dwEventType parameter contains additional information.
SERVICE_CONTROL_POWEREVENT
0x0000000D
Notifies a service of system power events. The dwEventType parameter contains additional information. If dwEventType is PBT_POWERSETTINGCHANGE, the lpEventData parameter also contains additional information.
SERVICE_CONTROL_SESSIONCHANGE
0x0000000E
Notifies a service of session change events. Note that a service will only be notified of a user logon if it is fully loaded before the logon attempt is made. The dwEventType and lpEventData parameters contain additional information.
SERVICE_CONTROL_TIMECHANGE
0x00000010
Notifies a service that the system time has changed. The lpEventData parameter contains additional information. The dwEventType parameter is not used.
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
SERVICE_CONTROL_TRIGGEREVENT
0x00000020
Notifies a service registered for a service trigger event that the event has occurred.
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
SERVICE_CONTROL_USERMODEREBOOT
0x00000040
Notifies a service that the user has initiated a reboot.
Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
- * - *

This parameter can also be a user-defined control code, as described in the following table.

- * - * - * - * - *
Control codeMeaning
Range 128 to 255.The service defines the action associated with the control code.
- * - * @param dwEventType The type of event that has occurred. This - * parameter is used if dwControl is - * SERVICE_CONTROL_DEVICEEVENT, - * SERVICE_CONTROL_HARDWAREPROFILECHANGE, - * SERVICE_CONTROL_POWEREVENT, or - * SERVICE_CONTROL_SESSIONCHANGE. Otherwise, it is - * zero. - * - *

If dwControl is SERVICE_CONTROL_DEVICEEVENT, this parameter can be - * one of the following values:

- * - * - * - *

If dwControl is SERVICE_CONTROL_HARDWAREPROFILECHANGE, this parameter - * can be one of the following values:

- * - * - * - *

If dwControl is SERVICE_CONTROL_POWEREVENT, this parameter can be one - * of the values specified in the wParam parameter of the - * WM_POWERBROADCAST message.

- * - *

If dwControl is SERVICE_CONTROL_SESSIONCHANGE, this parameter can be - * one of the values specified in the wParam parameter of the - * WM_WTSSESSION_CHANGE message.

- * - * @param lpEventData [in] Additional device information, if required. - * The format of this data depends on the value of - * the dwControl and dwEventType parameters. - * - *

If dwControl is SERVICE_CONTROL_DEVICEEVENT, this data corresponds to - * the lParam parameter that applications receive as part of a - * WM_DEVICECHANGE message.

- * - *

If dwControl is SERVICE_CONTROL_POWEREVENT and dwEventType is - * PBT_POWERSETTINGCHANGE, this data is a pointer to a - * POWERBROADCAST_SETTING structure.

- * - *

If dwControl is SERVICE_CONTROL_SESSIONCHANGE, this parameter is a - * pointer to a WTSSESSION_NOTIFICATION structure.

- * - *

If dwControl is SERVICE_CONTROL_TIMECHANGE, this data is a pointer to - * a SERVICE_TIMECHANGE_INFO structure.

- * - * @param lpContext [in] User-defined data passed from - * {@link RegisterServiceCtrlHandlerEx}. When multiple - * services share a process, the lpContext parameter - * can help identify the service. - * - * @return The return value for this function depends on the control - * code received. - * - *

The following list identifies the rules for this return value:

- * - * - */ - public int callback(int dwControl, int dwEventType, - Pointer lpEventData, Pointer lpContext); - } - - /** - * Specifies the ServiceMain function for a service that can run in the - * calling process. It is used by the StartServiceCtrlDispatcher function. - */ - public static class SERVICE_TABLE_ENTRY extends Structure { - - public static final List FIELDS = createFieldsOrder("lpServiceName", "lpServiceProc"); - /** - * The name of a service to be run in this service process. - * - *

- * If the service is installed with the SERVICE_WIN32_OWN_PROCESS - * service type, this member is ignored, but cannot be NULL. This member - * can be an empty string ("").

- *

- * If the service is installed with the SERVICE_WIN32_SHARE_PROCESS - * service type, this member specifies the name of the service that uses - * the ServiceMain function pointed to by the lpServiceProc member.

- */ - public String lpServiceName; - public SERVICE_MAIN_FUNCTION lpServiceProc; - - public SERVICE_TABLE_ENTRY() { - super(W32APITypeMapper.DEFAULT); - } - - @Override - protected List getFieldOrder() { - return FIELDS; - } - } - - /** - * - * Contains a service description. - * - *

- * The description of the service. If this member is NULL, the description - * remains unchanged. If this value is an empty string (""), the current - * description is deleted.

- *

- * The service description must not exceed the size of a registry value of - * type REG_SZ.

- *

- * This member can specify a localized string using the following - * format:

- *

- * @[path\]dllname,-strID

- *

- * The string with identifier strID is loaded from dllname; the path is - * optional. For more information, see RegLoadMUIString.

- *

- * Windows Server 2003 and Windows XP: Localized strings - * are not supported until Windows Vista.

- * - */ - public static class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info { - - public static final List FIELDS = createFieldsOrder("lpDescription"); - public String lpDescription; - - @Override - protected List getFieldOrder() { - return FIELDS; - } - } - - public static class SERVICE_STATUS_HANDLE extends WinNT.HANDLE { - - public SERVICE_STATUS_HANDLE() { - } - - public SERVICE_STATUS_HANDLE(Pointer p) { - super(p); - } - } -} diff --git a/contrib/ntservice/src/jnacontrib/win32/Win32Service.java b/contrib/ntservice/src/jnacontrib/win32/Win32Service.java index a6e991f13b..6d8cd39256 100644 --- a/contrib/ntservice/src/jnacontrib/win32/Win32Service.java +++ b/contrib/ntservice/src/jnacontrib/win32/Win32Service.java @@ -23,29 +23,35 @@ package jnacontrib.win32; -import jnacontrib.jna.*; import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Advapi32; import com.sun.jna.platform.win32.WinError; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.Winsvc; +import com.sun.jna.platform.win32.Winsvc.HandlerEx; import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_DESCRIPTION; +import com.sun.jna.platform.win32.Winsvc.SERVICE_MAIN_FUNCTION; import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY; import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; -import jnacontrib.jna.Advapi32.SERVICE_STATUS_HANDLE; -import jnacontrib.jna.Advapi32.SERVICE_TABLE_ENTRY; /** * Baseclass for a Win32 service. */ public abstract class Win32Service { + private static final Advapi32 advapi32 = Advapi32.INSTANCE; + + private final Object waitObject = new Object(); + protected String serviceName; private ServiceMain serviceMain; private ServiceControl serviceControl; private SERVICE_STATUS_HANDLE serviceStatusHandle; - private Object waitObject = new Object(); /** * Creates a new instance of Win32Service. @@ -104,34 +110,30 @@ public boolean install(String displayName, String description, String[] dependen * @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; - SC_HANDLE service, serviceManager; boolean success = false; - String dep = ""; + StringBuilder dep = new StringBuilder(); if(dependencies != null) { for(String s : dependencies) { - dep += s + "\0"; + dep.append(s); + dep.append("\0"); } } - dep += "\0"; + dep.append("\0"); - desc = new Advapi32.SERVICE_DESCRIPTION(); + SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION(); desc.lpDescription = description; - advapi32 = Advapi32.INSTANCE; - serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); if(serviceManager != null) { - service = advapi32.CreateService(serviceManager, serviceName, displayName, + SC_HANDLE service = advapi32.CreateService(serviceManager, serviceName, displayName, Winsvc.SERVICE_ALL_ACCESS, WinNT.SERVICE_WIN32_OWN_PROCESS, WinNT.SERVICE_DEMAND_START, WinNT.SERVICE_ERROR_NORMAL, command, - null, null, dep, account, password); + null, null, dep.toString(), account, password); if(service != null) { success = advapi32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc); @@ -139,25 +141,21 @@ public boolean install(String displayName, String description, String[] dependen } advapi32.CloseServiceHandle(serviceManager); } - return(success); + return success; } /** * Uninstall the service. * - * @throws java.lang.Exception * @return true on success */ public boolean uninstall() { - Advapi32 advapi32; - SC_HANDLE serviceManager, service; boolean success = false; - - advapi32 = Advapi32.INSTANCE; - serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); if(serviceManager != null) { - service = advapi32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS); + SC_HANDLE service = advapi32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS); if(service != null) { success = advapi32.DeleteService(service); @@ -165,7 +163,7 @@ public boolean uninstall() { } advapi32.CloseServiceHandle(serviceManager); } - return(success); + return success; } /** @@ -173,16 +171,12 @@ public boolean uninstall() { * @return true on success */ public boolean start() { - Advapi32 advapi32; - SC_HANDLE serviceManager, service; boolean success = false; - advapi32 = Advapi32.INSTANCE; - - serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); if(serviceManager != null) { - service = advapi32.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + SC_HANDLE service = advapi32.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); if(service != null) { success = advapi32.StartService(service, 0, null); @@ -191,51 +185,42 @@ public boolean start() { advapi32.CloseServiceHandle(serviceManager); } - return(success); + return success; } /** * Ask the ServiceControlManager to stop the service. * @return true on success */ - public boolean stop() throws Exception { - Advapi32 advapi32; - SC_HANDLE serviceManager, service; - 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 SERVICE_STATUS(); - success = advapi32.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus); - advapi32.CloseServiceHandle(service); - } - advapi32.CloseServiceHandle(serviceManager); + public boolean stop() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + + if (service != null) { + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); + success = Advapi32.INSTANCE.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus); + Advapi32.INSTANCE.CloseServiceHandle(service); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + + return (success); } - - return(success); - } /** * Initialize the service, connect to the ServiceControlManager. */ public void init() { - Advapi32 advapi32; - SERVICE_TABLE_ENTRY entry; - serviceMain = new ServiceMain(); - advapi32 = Advapi32.INSTANCE; - entry = new Advapi32.SERVICE_TABLE_ENTRY(); + SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY(); entry.lpServiceName = serviceName; entry.lpServiceProc = serviceMain; - advapi32.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) entry.toArray(2)); + Advapi32.INSTANCE.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) entry.toArray(2)); } /** @@ -246,12 +231,7 @@ public void init() { * @return handle to ServiceControlManager or null when failed */ private SC_HANDLE openServiceControlManager(String machine, int access) { - SC_HANDLE handle = null; - Advapi32 advapi32; - - advapi32 = Advapi32.INSTANCE; - handle = advapi32.OpenSCManager(machine, null, access); - return(handle); + return advapi32.OpenSCManager(machine, null, access); } /** @@ -262,11 +242,7 @@ private SC_HANDLE openServiceControlManager(String machine, int access) { * @param waitHint time to wait */ private void reportStatus(int status, int win32ExitCode, int waitHint) { - Advapi32 advapi32; - SERVICE_STATUS serviceStatus; - - advapi32 = Advapi32.INSTANCE; - serviceStatus = new SERVICE_STATUS(); + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); serviceStatus.dwServiceType = WinNT.SERVICE_WIN32_OWN_PROCESS; serviceStatus.dwControlsAccepted = Winsvc.SERVICE_ACCEPT_STOP | Winsvc.SERVICE_ACCEPT_SHUTDOWN; serviceStatus.dwWin32ExitCode = win32ExitCode; @@ -290,7 +266,7 @@ private void reportStatus(int status, int win32ExitCode, int waitHint) { /** * Implementation of the service main function. */ - private class ServiceMain implements Advapi32.SERVICE_MAIN_FUNCTION { + private class ServiceMain implements SERVICE_MAIN_FUNCTION { /** * Called when the service is starting. @@ -299,10 +275,6 @@ private class ServiceMain implements Advapi32.SERVICE_MAIN_FUNCTION { * @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); @@ -331,7 +303,7 @@ public void callback(int dwArgc, Pointer lpszArgv) { /** * Implementation of the service control function. */ - private class ServiceControl implements Advapi32.HandlerEx { + private class ServiceControl implements HandlerEx { /** * Called when the service get a control code. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java index f28b15a97c..ff70ca7ced 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java @@ -48,9 +48,12 @@ import com.sun.jna.platform.win32.WinReg.HKEY; import com.sun.jna.platform.win32.WinReg.HKEYByReference; import com.sun.jna.platform.win32.Winsvc.ChangeServiceConfig2Info; +import com.sun.jna.platform.win32.Winsvc.HandlerEx; import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE; import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS; +import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.LongByReference; import com.sun.jna.ptr.PointerByReference; @@ -2822,4 +2825,336 @@ boolean CreateProcessWithLogonW(String lpUsername, String lpDomain, String lpPas String lpApplicationName, String lpCommandLine, int dwCreationFlags, Pointer lpEnvironment, String lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInfo); + /** + * Connects the main thread of a service process to the service control + * manager, which causes the thread to be the service control dispatcher + * thread for the calling process. + * + * @param lpServiceTable A pointer to an array of SERVICE_TABLE_ENTRY + * structures containing one entry for each service + * that can execute in the calling process. The + * members of the last entry in the table must have + * NULL values to designate the end of the table. + * + * @return true if function succeeds. To get extended error information, call + * GetLastError. Possible error codes: + * + * + * + * + * + *
Return codeDescription
ERROR_FAILED_SERVICE_CONTROLLER_CONNECTThis error is returned if the program is being run as a console application rather than as a service. If the program will be run as a console application for debugging purposes, structure it such that service-specific code is not called when this error is returned.
ERROR_INVALID_DATAThe specified dispatch table contains entries that are not in the proper format.
ERROR_SERVICE_ALREADY_RUNNINGThe process has already called StartServiceCtrlDispatcher. Each process can call StartServiceCtrlDispatcher only one time.
+ */ + public boolean StartServiceCtrlDispatcher(SERVICE_TABLE_ENTRY[] lpServiceTable); + + /** + * Registers a function to handle service control requests. + * + *

This function has been superseded by the RegisterServiceCtrlHandlerEx + * function. A service can use either function, but the new function + * supports user-defined context data, and the new handler function supports + * additional extended control codes.

+ * + * @param lpServiceName The name of the service run by the calling thread. + * This is the service name that the service control + * program specified in the CreateService function when + * creating the service. + * + *

If the service type is SERVICE_WIN32_OWN_PROCESS, + * the function does not verify that the specified name + * is valid, because there is only one registered + * service in the process.

+ * + * @param lpHandlerProc A pointer to the handler function to be registered. + * For more information, see {@link Handler}. + * + * @return A service status handle, NULL on error. Call GetLastError to + * get extended error condition. Possible error codes: + * + * + * + * + *
Return codeDescription
ERROR_NOT_ENOUGH_MEMORYNot enough memory is available to convert an ANSI string parameter to Unicode. This error does not occur for Unicode string parameters.
ERROR_SERVICE_NOT_IN_EXEThe service entry was specified incorrectly when the process called the {@link #StartServiceCtrlDispatcher} function.
+ */ + public SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(String lpServiceName, + Handler lpHandlerProc); + + /** + * Registers a function to handle extended service control requests. + * + * @param lpServiceName The name of the service run by the calling thread. + * This is the service name that the service control + * program specified in the CreateService function when + * creating the service. + * @param lpHandlerProc The handler function to be registered. + * For more information, see HandlerEx. + * @param lpContext Any user-defined data. This parameter, which is + * passed to the handler function, can help identify + * the service when multiple services share a process. + * + * @return A service status handle on success, NULL on error. Call GetLastError + * to get extended information. Possible error codes: + * + * + * + * + *
Return codeDescription
ERROR_NOT_ENOUGH_MEMORYNot enough memory is available to convert an ANSI string parameter to Unicode. This error does not occur for Unicode string parameters.
ERROR_SERVICE_NOT_IN_EXEThe service entry was specified incorrectly when the process called the {@link #StartServiceCtrlDispatcher} function.
+ */ + public SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerEx(String lpServiceName, + HandlerEx lpHandlerProc, Pointer lpContext); + + /** + * Updates the service control manager's status information for the calling + * service. + * + * + * @param hServiceStatus A handle to the status information structure for + * the current service. This handle is returned by + * the RegisterServiceCtrlHandlerEx function. + * @param lpServiceStatus A pointer to the SERVICE_STATUS structure the + * contains the latest status information for the + * calling service. + * + * @return true if function succeeds. To get extended error information, call + * GetLastError. Possible error codes: + * + * + * + * + *
Return codeDescription
ERROR_INVALID_DATAThe specified service status structure is invalid.
ERROR_INVALID_HANDLEThe specified handle is invalid.
+ */ + public boolean SetServiceStatus(SERVICE_STATUS_HANDLE hServiceStatus, + SERVICE_STATUS lpServiceStatus); + + /** + * Creates a service object and adds it to the specified service control + * manager database. + * + * @param hSCManager [in] A handle to the service control manager + * database. This handle is returned by the + * OpenSCManager function and must have the + * SC_MANAGER_CREATE_SERVICE access right. For + * more information, see Service Security and + * Access Rights. + * @param lpServiceName [in] The name of the service to install. The + * maximum string length is 256 characters. The + * service control manager database preserves the + * case of the characters, but service name + * comparisons are always case insensitive. + * Forward-slash (/) and backslash (\) are not + * valid service name characters. + * @param lpDisplayName [in, optional] The display name to be used by + * user interface programs to identify the + * service. This string has a maximum length of + * 256 characters. The name is case-preserved in + * the service control manager. Display name + * comparisons are always case-insensitive. + * @param dwDesiredAccess [in] The access to the service. Before granting + * the requested access, the system checks the + * access token of the calling process. For a list + * of values, see Service Security and Access + * Rights. + * @param dwServiceType [in] The service type. This parameter can be + * one of the following values. + * + * + * + * + * + * + * + * + *
ValueMeaning
SERVICE_ADAPTER
0x00000004
Reserved.
SERVICE_FILE_SYSTEM_DRIVER
0x00000002
File system driver service.
SERVICE_KERNEL_DRIVER
0x00000001
Driver service.
SERVICE_RECOGNIZER_DRIVER
0x00000008
Reserved.
SERVICE_WIN32_OWN_PROCESS
0x00000010
Service that runs in its own process.
SERVICE_WIN32_SHARE_PROCESS
0x00000020
Service that shares a process with one or more other services. For more information, see Service Programs.
+ * + *

If you specify either SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS, and the service is running in the context of the LocalSystem account, you can also specify the following value.

+ * + * + * + * + *
ValueMeaning
SERVICE_INTERACTIVE_PROCESS
0x00000100
The service can interact with the desktop.
+ * + * @param dwStartType [in] The service start options. This parameter + * can be one of the following values. + * + * + * + * + * + * + * + * + *
ValueMeaning
SERVICE_AUTO_START
0x00000002
A service started automatically by the service control manager during system startup.
SERVICE_BOOT_START
0x00000000
A device driver started by the system loader. This value is valid only for driver services.
SERVICE_DEMAND_START
0x00000003
A service started by the service control manager when a process calls the StartService function.
SERVICE_DISABLED
0x00000004
A service that cannot be started. Attempts to start the service result in the error code ERROR_SERVICE_DISABLED.
SERVICE_SYSTEM_START
0x00000001
A device driver started by the IoInitSystem function. This value is valid only for driver services.
+ * + * @param dwErrorControl [in] The severity of the error, and action + * taken, if this service fails to start. This + * parameter can be one of the following values. + * + * + * + * + * + * + * + *
ValueMeaning
SERVICE_ERROR_CRITICAL
0x00000003
The startup program logs the error in the event log, if possible. If the last-known-good configuration is being started, the startup operation fails. Otherwise, the system is restarted with the last-known good configuration.
SERVICE_ERROR_IGNORE
0x00000000
The startup program ignores the error and continues the startup operation.
SERVICE_ERROR_NORMAL
0x00000001
The startup program logs the error in the event log but continues the startup operation.
SERVICE_ERROR_SEVERE
0x00000002
The startup program logs the error in the event log. If the last-known-good configuration is being started, the startup operation continues. Otherwise, the system is restarted with the last-known-good configuration.
+ * + * @param lpBinaryPathName [in, optional] The fully qualified path to the + * service binary file. If the path contains a + * space, it must be quoted so that it is + * correctly interpreted. For example, "d:\\my + * share\\myservice.exe" should be specified as + * "\"d:\\my share\\myservice.exe\"". + * + *

The path can also include arguments for an + * auto-start service. For example, + * "d:\\myshare\\myservice.exe arg1 arg2". These + * passed to the service entry point (typically + * the main function).

+ * + *

If you specify a path on another computer, + * the share must be accessible by the computer + * account of the local computer because this is + * the security context used in the remote call. + * However, this requirement allows any potential + * vulnerabilities in the remote computer to + * affect the local computer. Therefore, it is + * best to use a local file.

+ * + * @param lpLoadOrderGroup [in, optional] The names of the load ordering + * group of which this service is a member. + * Specify NULL or an empty string if the service + * does not belong to a group. + * + *

The startup program uses load ordering + * groups to load groups of services in a + * specified order with respect to the other + * groups. The list of load ordering groups is + * contained in the following registry value:

+ * + *

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder

+ * @param lpdwTagId [out, optional] A pointer to a variable that + * receives a tag value that is unique in the + * group specified in the lpLoadOrderGroup + * parameter. Specify NULL if you are not changing + * the existing tag. + * + *

You can use a tag for ordering service + * startup within a load ordering group by + * specifying a tag order vector in the following + * registry value:

+ * + *

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList

+ * + *

Tags are only evaluated for driver services + * that have SERVICE_BOOT_START or + * SERVICE_SYSTEM_START start types.

+ * @param lpDependencies [in, optional] A pointer to a double + * null-terminated array of null-separated names + * of services or load ordering groups that the + * system must start before this service. Specify + * NULL or an empty string if the service has no + * dependencies. Dependency on a group means that + * this service can run if at least one member of + * the group is running after an attempt to start + * all members of the group. + * + *

You must prefix group names with + * SC_GROUP_IDENTIFIER so that they can be + * distinguished from a service name, because + * services and service groups share the same name + * space.

+ * @param lpServiceStartName [in, optional] The name of the account under + * which the service should run. If the service + * type is SERVICE_WIN32_OWN_PROCESS, use an + * account name in the form DomainName\UserName. + * The service process will be logged on as this + * user. If the account belongs to the built-in + * domain, you can specify .\UserName. + * + *

If this parameter is NULL, CreateService + * uses the LocalSystem account. If the service + * type specifies SERVICE_INTERACTIVE_PROCESS, the + * service must run in the LocalSystem account.

+ * + *

If this parameter is NT AUTHORITY\LocalService, + * CreateService uses the LocalService account. If + * the parameter is NT AUTHORITY\NetworkService, + * CreateService uses the NetworkService account.

+ * + *

A shared process can run as any user.

+ * + *

If the service type is SERVICE_KERNEL_DRIVER + * or SERVICE_FILE_SYSTEM_DRIVER, the name is the + * driver object name that the system uses to load + * the device driver. Specify NULL if the driver + * is to use a default object name created by the + * I/O system.

+ * + *

A service can be configured to use a managed + * account or a virtual account. If the service is + * configured to use a managed service account, + * the name is the managed service account name. + * If the service is configured to use a virtual + * account, specify the name as NT + * SERVICE\ServiceName. For more information about + * managed service accounts and virtual accounts, + * see the Service Accounts Step-by-Step Guide. + * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: + * Managed service accounts and virtual accounts are not supported until + * Windows 7 and Windows Server 2008 R2.

+ * @param lpPassword [in, optional] The password to the account name + * specified by the lpServiceStartName parameter. + * Specify an empty string if the account has no + * password or if the service runs in the + * LocalService, NetworkService, or LocalSystem + * account. For more information, see Service + * Record List. + * + *

If the account name specified by the + * lpServiceStartName parameter is the name of a + * managed service account or virtual account + * name, the lpPassword parameter must be NULL.

+ * + *

Passwords are ignored for driver services.

+ * + * @return SC_HANDLE on success, NULL on error. Call GetLastError to + * get extended error condition. Possible error codes: + * + * + * + * + * + * + * + * + * + * + * + * + *
Return codeDescription
ERROR_ACCESS_DENIEDThe handle to the SCM database does not have the SC_MANAGER_CREATE_SERVICE access right.
ERROR_CIRCULAR_DEPENDENCYA circular service dependency was specified.
ERROR_DUPLICATE_SERVICE_NAMEThe display name already exists in the service control manager database either as a service name or as another display name.
ERROR_INVALID_HANDLEThe handle to the specified service control manager database is invalid.
ERROR_INVALID_NAMEThe specified service name is invalid.
ERROR_INVALID_PARAMETERA parameter that was specified is invalid.
ERROR_INVALID_SERVICE_ACCOUNTThe user account name specified in the lpServiceStartName parameter does not exist.
ERROR_SERVICE_EXISTSThe specified service already exists in this database.
ERROR_SERVICE_MARKED_FOR_DELETEThe specified service already exists in this database and has been marked for deletion.
+ */ + public SC_HANDLE CreateService(SC_HANDLE hSCManager, String lpServiceName, + String lpDisplayName, int dwDesiredAccess, int dwServiceType, + int dwStartType, int dwErrorControl, String lpBinaryPathName, + String lpLoadOrderGroup, IntByReference lpdwTagId, + String lpDependencies, String lpServiceStartName, String lpPassword); + + /** + * Marks the specified service for deletion from the service control manager database. + * + * @param hService [in] A handle to the service. This handle is returned by + * the OpenService or CreateService function, and it must + * have the DELETE access right. + * + * @return true if function succeeds. To get extended error information, call + * GetLastError. Possible error codes: + * + * + * + * + * + * + *
Return codeDescription
ERROR_ACCESS_DENIEDThe handle does not have the DELETE access right.
ERROR_INVALID_HANDLEThe specified handle is invalid.
ERROR_SERVICE_MARKED_FOR_DELETEThe specified service has already been marked for deletion.
+ */ + public boolean DeleteService(SC_HANDLE hService); } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/W32Service.java b/contrib/platform/src/com/sun/jna/platform/win32/W32Service.java index f50b7b7987..2e5702b172 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/W32Service.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/W32Service.java @@ -27,13 +27,8 @@ import java.util.List; import com.sun.jna.Memory; -import com.sun.jna.Native; import com.sun.jna.Pointer; -import com.sun.jna.Structure; -import com.sun.jna.WString; import com.sun.jna.platform.win32.WinDef.DWORD; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.platform.win32.WinNT.LUID; import com.sun.jna.platform.win32.WinNT.LUID_AND_ATTRIBUTES; diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Winsvc.java b/contrib/platform/src/com/sun/jna/platform/win32/Winsvc.java index 4c63eb5799..cfed9aa5f5 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Winsvc.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Winsvc.java @@ -30,6 +30,7 @@ import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APITypeMapper; /** @@ -379,14 +380,60 @@ public static class SC_HANDLE extends HANDLE { } // // Service object specific access type // + /** + * Required to call the QueryServiceConfig and + * {@link com.sun.jna.platform.win32.Advapi32#QueryServiceConfig2} + * functions to query the service configuration. + */ int SERVICE_QUERY_CONFIG = 0x0001; + /** + * Required to call the ChangeServiceConfig or + * {@link com.sun.jna.platform.win32.Advapi32#ChangeServiceConfig2} function + * to change the service configuration. Because this grants the caller the + * right to change the executable file that the system runs, it should be + * granted only to administrators. + */ int SERVICE_CHANGE_CONFIG = 0x0002; + /** + * Required to call the QueryServiceStatus or + * {@link com.sun.jna.platform.win32.Advapi32#QueryServiceStatusEx} function + * to ask the service control manager about the status of the service. + * + *

+ * Required to call the NotifyServiceStatusChange function to receive + * notification when a service changes status.

+ */ int SERVICE_QUERY_STATUS = 0x0004; int SERVICE_ENUMERATE_DEPENDENTS = 0x0008; + /** + * Required to call the + * {@link com.sun.jna.platform.win32.Advapi32#StartService} function to + * start the service. + */ int SERVICE_START = 0x0010; - int SERVICE_STOP = 0x0020; - int SERVICE_PAUSE_CONTINUE = 0x0040; - int SERVICE_INTERROGATE = 0x0080; + /** + * Required to call the + * {@link com.sun.jna.platform.win32.Advapi32#ControlService} function to + * stop the service. + */ + int SERVICE_STOP = 0x0020; + /** + * Required to call the + * {@link com.sun.jna.platform.win32.Advapi32#ControlService} function to + * pause or continue the service. + */ + int SERVICE_PAUSE_CONTINUE = 0x0040; + /** + * Required to call the + * {@link com.sun.jna.platform.win32.Advapi32#ControlService} function to + * pause or continue the service. + */ + int SERVICE_INTERROGATE = 0x0080; + /** + * Required to call the + * {@link com.sun.jna.platform.win32.Advapi32#ControlService} function to + * ask the service to report its status immediately. + */ int SERVICE_USER_DEFINED_CONTROL = 0x0100; int SERVICE_ALL_ACCESS = @@ -399,15 +446,63 @@ public static class SC_HANDLE extends HANDLE { } // // Controls // + /** + * Notifies a service that it should stop. The hService handle must have the + * {@link #SERVICE_STOP} access right. + * + *

After sending the stop request to a service, you should not send other + * controls to the service.

+ */ int SERVICE_CONTROL_STOP = 0x00000001; + /** + * Notifies a service that it should pause. The hService handle must have + * the {@link #SERVICE_PAUSE_CONTINUE} access right. + */ int SERVICE_CONTROL_PAUSE = 0x00000002; + /** + * Notifies a service that its startup parameters have changed. The hService + * handle must have the {@link #SERVICE_PAUSE_CONTINUE} access right. + */ int SERVICE_CONTROL_CONTINUE = 0x00000003; + /** + * Notifies a service that it should report its current status information + * to the service control manager. The hService handle must have the + * {@link #SERVICE_INTERROGATE} access right. + * + *

+ * Note that this control is not generally useful as the SCM is aware of the + * current state of the service.

+ */ int SERVICE_CONTROL_INTERROGATE = 0x00000004; int SERVICE_CONTROL_SHUTDOWN = 0x00000005; int SERVICE_CONTROL_PARAMCHANGE = 0x00000006; + /** + * Notifies a network service that there is a new component for binding. The + * hService handle must have the {@link #SERVICE_PAUSE_CONTINUE} access + * right. However, this control code has been deprecated; use Plug and Play + * functionality instead. + */ int SERVICE_CONTROL_NETBINDADD = 0x00000007; + /** + * Notifies a network service that a component for binding has been removed. + * The hService handle must have the {@link #SERVICE_PAUSE_CONTINUE} access + * right. However, this control code has been deprecated; use Plug and Play + * functionality instead. + */ int SERVICE_CONTROL_NETBINDREMOVE = 0x00000008; + /** + * Notifies a network service that a disabled binding has been enabled. The + * hService handle must have the {@link #SERVICE_PAUSE_CONTINUE} access + * right. However, this control code has been deprecated; use Plug and Play + * functionality instead. + */ int SERVICE_CONTROL_NETBINDENABLE = 0x00000009; + /** + * Notifies a network service that one of its bindings has been disabled. + * The hService handle must have the {@link #SERVICE_PAUSE_CONTINUE} access + * right. However, this control code has been deprecated; use Plug and Play + * functionality instead. + */ int SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A; int SERVICE_CONTROL_DEVICEEVENT = 0x0000000B; int SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0x0000000C; @@ -472,4 +567,284 @@ public static class SC_HANDLE extends HANDLE { } public abstract class SC_STATUS_TYPE { public static final int SC_STATUS_PROCESS_INFO = 0; } + + /** + * The entry point for a service. + */ + interface SERVICE_MAIN_FUNCTION extends StdCallLibrary.StdCallCallback { + + /** + * + * @param dwArgc [in] The number of arguments in the lpszArgv array. + * @param lpszArgv [in] The null-terminated argument strings passed to + * the service by the call to the StartService function + * that started the service. If there are no arguments, + * this parameter can be NULL. Otherwise, the first + * argument (lpszArgv[0]) is the name of the service, + * followed by any additional arguments (lpszArgv[1] + * through lpszArgv[dwArgc-1]). + * + *

If the user starts a manual service using the + * Services snap-in from the Control Panel, the strings + * for the lpszArgv parameter come from the properties + * dialog box for the service (from the Services snap-in, + * right-click the service entry, click Properties, and + * enter the parameters in Start parameters.) + */ + public void callback(int dwArgc, Pointer lpszArgv); + } + + /** + * An application-defined callback function used with the + * RegisterServiceCtrlHandler function. A service program can use it as the + * control handler function of a particular service. + * + *

+ * This function has been superseded by the {@link HandlerEx} control handler + * function used with the {@link #RegisterServiceCtrlHandlerEx} function. A service + * can use either control handler, but the new control handler supports + * user-defined context data and additional extended control codes.

+ */ + interface Handler extends StdCallLibrary.StdCallCallback { + + /** + * @param fdwControl [in] The control code. This parameter can be one of + * the following values. + * + * + * + * + * + * + * + * + * + * + * + * + *
Control codeMeaning
SERVICE_CONTROL_CONTINUE
0x00000003
Notifies a paused service that it should resume.
SERVICE_CONTROL_INTERROGATE
0x00000004
Notifies a service that it should report its current status information to the service control manager.
The handler should simply return NO_ERROR; the SCM is aware of the current state of the service.
SERVICE_CONTROL_NETBINDADD
0x00000007
Notifies a network service that there is a new component for binding. The service should bind to the new component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDDISABLE
0x0000000A
Notifies a network service that one of its bindings has been disabled. The service should reread its binding information and remove the binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDENABLE
0x00000009
Notifies a network service that a disabled binding has been enabled. The service should reread its binding information and add the new binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDREMOVE
0x00000008
Notifies a network service that a component for binding has been removed. The service should reread its binding information and unbind from the removed component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_PARAMCHANGE
0x00000006
Notifies a service that its startup parameters have changed. The service should reread its startup parameters.
SERVICE_CONTROL_PAUSE
0x00000002
Notifies a service that it should pause.
SERVICE_CONTROL_SHUTDOWN
0x00000005
Notifies a service that the system is shutting down so the service can perform cleanup tasks.
If a service accepts this control code, it must stop after it performs its cleanup tasks and return NO_ERROR. After the SCM sends this control code, it will not send other control codes to the service.
SERVICE_CONTROL_STOP
0x00000001
Notifies a service that it should stop.
If a service accepts this control code, it must stop upon receipt and return NO_ERROR. After the SCM sends this control code, it does not send other control codes.
Windows XP: If the service returns NO_ERROR and continues to run, it continues to receive control codes. This behavior changed starting with Windows Server 2003 and Windows XP with SP2.
+ * + *

This parameter can also be a user-defined control code, as described in the following table.

+ * + * + * + * + *
Control codeMeaning
Range 128 to 255.The service defines the action associated with the control code.
+ */ + public void callback(int fdwControl); + } + + /** + * An application-defined callback function used with the + * RegisterServiceCtrlHandlerEx function. A service program can use it as + * the control handler function of a particular service. + * + *

This function supersedes the Handler control handler function used + * with the {@link RegisterServiceCtrlHandler} function. A service can use either + * control handler, but the new control handler supports user-defined + * context data and additional extended control codes.

+ */ + interface HandlerEx extends StdCallLibrary.StdCallCallback { + + /** + * @param dwControl [in] The control code. This parameter can be one of + * the following values. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Control codeMeaning
SERVICE_CONTROL_CONTINUE
0x00000003
Notifies a paused service that it should resume.
SERVICE_CONTROL_INTERROGATE
0x00000004
Notifies a service that it should report its current status information to the service control manager.
The handler should simply return NO_ERROR; the SCM is aware of the current state of the service.
SERVICE_CONTROL_NETBINDADD
0x00000007
Notifies a network service that there is a new component for binding. The service should bind to the new component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDDISABLE
0x0000000A
Notifies a network service that one of its bindings has been disabled. The service should reread its binding information and remove the binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDENABLE
0x00000009
Notifies a network service that a disabled binding has been enabled. The service should reread its binding information and add the new binding.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_NETBINDREMOVE
0x00000008
Notifies a network service that a component for binding has been removed. The service should reread its binding information and unbind from the removed component.
Applications should use Plug and Play functionality instead.
SERVICE_CONTROL_PARAMCHANGE
0x00000006
Notifies a service that its startup parameters have changed. The service should reread its startup parameters.
SERVICE_CONTROL_PAUSE
0x00000002
Notifies a service that it should pause.
SERVICE_CONTROL_PRESHUTDOWN
0x0000000F
Notifies a service that the system will be shutting down. Services that need additional time to perform cleanup tasks beyond the tight time restriction at system shutdown can use this notification. The service control manager sends this notification to applications that have registered for it before sending a SERVICE_CONTROL_SHUTDOWN notification to applications that have registered for that notification.
A service that handles this notification blocks system shutdown until the service stops or the preshutdown time-out interval specified through SERVICE_PRESHUTDOWN_INFO expires. Because this affects the user experience, services should use this feature only if it is absolutely necessary to avoid data loss or significant recovery time at the next system start.
Windows Server 2003 and Windows XP: This value is not supported.
SERVICE_CONTROL_SHUTDOWN
0x00000005
Notifies a service that the system is shutting down so the service can perform cleanup tasks.
If a service accepts this control code, it must stop after it performs its cleanup tasks and return NO_ERROR. After the SCM sends this control code, it will not send other control codes to the service.
SERVICE_CONTROL_STOP
0x00000001
Notifies a service that it should stop.
If a service accepts this control code, it must stop upon receipt and return NO_ERROR. After the SCM sends this control code, it does not send other control codes.
Windows XP: If the service returns NO_ERROR and continues to run, it continues to receive control codes. This behavior changed starting with Windows Server 2003 and Windows XP with SP2.
+ * + *

This parameter can also be one of the following extended control codes. Note that these control codes are not supported by the {@link Handler} function.

+ * + * + * + * + * + * + * + * + * + * + *
Control codeMeaning
SERVICE_CONTROL_DEVICEEVENT
0x0000000B
Notifies a service of device events. (The service must have registered to receive these notifications using the RegisterDeviceNotification function.) The dwEventType and lpEventData parameters contain additional information.
SERVICE_CONTROL_HARDWAREPROFILECHANGE
0x0000000C
Notifies a service that the computer's hardware profile has changed. The dwEventType parameter contains additional information.
SERVICE_CONTROL_POWEREVENT
0x0000000D
Notifies a service of system power events. The dwEventType parameter contains additional information. If dwEventType is PBT_POWERSETTINGCHANGE, the lpEventData parameter also contains additional information.
SERVICE_CONTROL_SESSIONCHANGE
0x0000000E
Notifies a service of session change events. Note that a service will only be notified of a user logon if it is fully loaded before the logon attempt is made. The dwEventType and lpEventData parameters contain additional information.
SERVICE_CONTROL_TIMECHANGE
0x00000010
Notifies a service that the system time has changed. The lpEventData parameter contains additional information. The dwEventType parameter is not used.
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
SERVICE_CONTROL_TRIGGEREVENT
0x00000020
Notifies a service registered for a service trigger event that the event has occurred.
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
SERVICE_CONTROL_USERMODEREBOOT
0x00000040
Notifies a service that the user has initiated a reboot.
Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This control code is not supported.
+ * + *

This parameter can also be a user-defined control code, as described in the following table.

+ * + * + * + * + *
Control codeMeaning
Range 128 to 255.The service defines the action associated with the control code.
+ * + * @param dwEventType The type of event that has occurred. This + * parameter is used if dwControl is + * SERVICE_CONTROL_DEVICEEVENT, + * SERVICE_CONTROL_HARDWAREPROFILECHANGE, + * SERVICE_CONTROL_POWEREVENT, or + * SERVICE_CONTROL_SESSIONCHANGE. Otherwise, it is + * zero. + * + *

If dwControl is SERVICE_CONTROL_DEVICEEVENT, this parameter can be + * one of the following values:

+ * + *
    + *
  • DBT_DEVICEARRIVAL
  • + *
  • DBT_DEVICEREMOVECOMPLETE
  • + *
  • DBT_DEVICEQUERYREMOVE
  • + *
  • DBT_DEVICEQUERYREMOVEFAILED
  • + *
  • DBT_DEVICEREMOVEPENDING DBT_CUSTOMEVENT
  • + *
+ * + *

If dwControl is SERVICE_CONTROL_HARDWAREPROFILECHANGE, this parameter + * can be one of the following values:

+ * + *
    + *
  • DBT_CONFIGCHANGED
  • + *
  • DBT_QUERYCHANGECONFIG
  • + *
  • DBT_CONFIGCHANGECANCELED
  • + *
+ * + *

If dwControl is SERVICE_CONTROL_POWEREVENT, this parameter can be one + * of the values specified in the wParam parameter of the + * WM_POWERBROADCAST message.

+ * + *

If dwControl is SERVICE_CONTROL_SESSIONCHANGE, this parameter can be + * one of the values specified in the wParam parameter of the + * WM_WTSSESSION_CHANGE message.

+ * + * @param lpEventData [in] Additional device information, if required. + * The format of this data depends on the value of + * the dwControl and dwEventType parameters. + * + *

If dwControl is SERVICE_CONTROL_DEVICEEVENT, this data corresponds to + * the lParam parameter that applications receive as part of a + * WM_DEVICECHANGE message.

+ * + *

If dwControl is SERVICE_CONTROL_POWEREVENT and dwEventType is + * PBT_POWERSETTINGCHANGE, this data is a pointer to a + * POWERBROADCAST_SETTING structure.

+ * + *

If dwControl is SERVICE_CONTROL_SESSIONCHANGE, this parameter is a + * pointer to a WTSSESSION_NOTIFICATION structure.

+ * + *

If dwControl is SERVICE_CONTROL_TIMECHANGE, this data is a pointer to + * a SERVICE_TIMECHANGE_INFO structure.

+ * + * @param lpContext [in] User-defined data passed from + * {@link RegisterServiceCtrlHandlerEx}. When multiple + * services share a process, the lpContext parameter + * can help identify the service. + * + * @return The return value for this function depends on the control + * code received. + * + *

The following list identifies the rules for this return value:

+ * + *
    + *
  • In general, if your service does not handle the control, return + * ERROR_CALL_NOT_IMPLEMENTED. However, your service should return + * NO_ERROR for SERVICE_CONTROL_INTERROGATE even if your service does + * not handle it.
  • + *
  • If your service handles SERVICE_CONTROL_STOP or + * SERVICE_CONTROL_SHUTDOWN, return NO_ERROR.
  • + *
  • If your service handles SERVICE_CONTROL_DEVICEEVENT, return + * NO_ERROR to grant the request and an error code to deny the + * request.
  • + *
  • If your service handles SERVICE_CONTROL_HARDWAREPROFILECHANGE, + * return NO_ERROR to grant the request and an error code to deny the + * request.<
  • If your service handles + * SERVICE_CONTROL_POWEREVENT, return NO_ERROR to grant the request and + * an error code to deny the request.
  • + *
  • For all other control codes your service handles, return + * NO_ERROR.
  • + *
+ */ + public int callback(int dwControl, int dwEventType, + Pointer lpEventData, Pointer lpContext); + } + + /** + * Specifies the ServiceMain function for a service that can run in the + * calling process. It is used by the StartServiceCtrlDispatcher function. + */ + public static class SERVICE_TABLE_ENTRY extends Structure { + + public static final List FIELDS = createFieldsOrder("lpServiceName", "lpServiceProc"); + /** + * The name of a service to be run in this service process. + * + *

+ * If the service is installed with the SERVICE_WIN32_OWN_PROCESS + * service type, this member is ignored, but cannot be NULL. This member + * can be an empty string ("").

+ *

+ * If the service is installed with the SERVICE_WIN32_SHARE_PROCESS + * service type, this member specifies the name of the service that uses + * the ServiceMain function pointed to by the lpServiceProc member.

+ */ + public String lpServiceName; + public SERVICE_MAIN_FUNCTION lpServiceProc; + + public SERVICE_TABLE_ENTRY() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + /** + * + * Contains a service description. + * + *

+ * The description of the service. If this member is NULL, the description + * remains unchanged. If this value is an empty string (""), the current + * description is deleted.

+ *

+ * The service description must not exceed the size of a registry value of + * type REG_SZ.

+ *

+ * This member can specify a localized string using the following + * format:

+ *

+ * @[path\]dllname,-strID

+ *

+ * The string with identifier strID is loaded from dllname; the path is + * optional. For more information, see RegLoadMUIString.

+ *

+ * Windows Server 2003 and Windows XP: Localized strings + * are not supported until Windows Vista.

+ * + */ + public static class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info { + + public static final List FIELDS = createFieldsOrder("lpDescription"); + public String lpDescription; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class SERVICE_STATUS_HANDLE extends WinNT.HANDLE { + + public SERVICE_STATUS_HANDLE() { + } + + public SERVICE_STATUS_HANDLE(Pointer p) { + super(p); + } + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/W32ServiceTest.java b/contrib/platform/test/com/sun/jna/platform/win32/W32ServiceTest.java index 5c8fe8e13a..9a6aba69b5 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/W32ServiceTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/W32ServiceTest.java @@ -10,7 +10,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ - package com.sun.jna.platform.win32; import java.util.List; @@ -22,119 +21,124 @@ import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS; import com.sun.jna.platform.win32.Winsvc.SERVICE_FAILURE_ACTIONS; -public class W32ServiceTest extends TestCase{ - W32ServiceManager _serviceManager = new W32ServiceManager(); - - public void setUp() { - _serviceManager.open(Winsvc.SC_MANAGER_CONNECT); - } - - public void tearDown() { - _serviceManager.close(); - } - - public void testStartService() { - // TODO implement a test service and enable this test -// W32Service service = _serviceManager.openService("eventlog", Winsvc.SERVICE_ALL_ACCESS); -// service.startService(); -// assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_RUNNING); -// service.close(); - } - - public void testStopService() { - // TODO implement a test service and enable this test -// W32Service service = _serviceManager.openService("eventlog", Winsvc.SERVICE_ALL_ACCESS); -// service.stopService(); -// assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_STOPPED); -// service.close(); - } - - - public void testPauseService() { - // TODO implement a test service and enable this test -// W32Service service = _serviceManager.openService("MSSQL$SQLEXPRESS", Winsvc.SERVICE_ALL_ACCESS); -// service.pauseService(); -// assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_PAUSED); -// service.close(); - } - - public void testContinueService() { - // TODO implement a test service and enable this test -// W32Service service = _serviceManager.openService("MSSQL$SQLEXPRESS", Winsvc.SERVICE_ALL_ACCESS); -// service.continueService(); -// assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_RUNNING); -// service.close(); - } - - public void testQueryStatus() { - W32Service service = _serviceManager.openService("eventlog", Winsvc.SERVICE_QUERY_STATUS); - SERVICE_STATUS_PROCESS status = service.queryStatus(); - assertTrue(status.dwCurrentState == Winsvc.SERVICE_RUNNING || - status.dwCurrentState == Winsvc.SERVICE_STOPPED); - service.close(); - } - - public void testSetAndGetFailureActions() { - final String svcId = "w32time"; - final String rebootMsg = "Restarting " + svcId + " due to service failure"; - final String command = "echo " + svcId + " failure"; - final int resetPeriod = 5000; - - W32Service service = _serviceManager.openService(svcId, Winsvc.SC_MANAGER_ALL_ACCESS); - SERVICE_FAILURE_ACTIONS prevActions = service.getFailureActions(); - - List actions = new LinkedList(); - - SC_ACTION action = new SC_ACTION(); - action.type = Winsvc.SC_ACTION_RESTART; - action.delay = 1000; - actions.add(action); - - action = new SC_ACTION(); - action.type = Winsvc.SC_ACTION_REBOOT; - action.delay = 2000; - actions.add(action); - - action = new SC_ACTION(); - action.type = Winsvc.SC_ACTION_RUN_COMMAND; - action.delay = 3000; - actions.add(action); - - action = new SC_ACTION(); - action.type = Winsvc.SC_ACTION_NONE; - action.delay = 4000; - actions.add(action); - - service.setFailureActions(actions, resetPeriod, rebootMsg, command); - - SERVICE_FAILURE_ACTIONS changedActions = service.getFailureActions(); - assertEquals(changedActions.lpRebootMsg, rebootMsg); - assertEquals(changedActions.lpCommand, command); - assertEquals(changedActions.dwResetPeriod, resetPeriod); - assertEquals(changedActions.cActions, 4); - SC_ACTION[] actionArray = (SC_ACTION[])changedActions.lpsaActions.toArray(changedActions.cActions); - assertEquals(actionArray[0].type, Winsvc.SC_ACTION_RESTART); - assertEquals(actionArray[0].delay, 1000); - assertEquals(actionArray[1].type, Winsvc.SC_ACTION_REBOOT); - assertEquals(actionArray[1].delay, 2000); - assertEquals(actionArray[2].type, Winsvc.SC_ACTION_RUN_COMMAND); - assertEquals(actionArray[2].delay, 3000); - assertEquals(actionArray[3].type, Winsvc.SC_ACTION_NONE); - assertEquals(actionArray[3].delay, 4000); - - // restore old settings - Advapi32.INSTANCE.ChangeServiceConfig2(service._handle, Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS, - prevActions); - - service.close(); - } - - public void testSetFailureActionsFlag() { - W32Service service = _serviceManager.openService("eventlog", Winsvc.SC_MANAGER_ALL_ACCESS); - boolean prevFlag = service.getFailureActionsFlag(); - service.setFailureActionsFlag(!prevFlag); - assertTrue(prevFlag != service.getFailureActionsFlag()); - service.setFailureActionsFlag(prevFlag); - service.close(); - } +public class W32ServiceTest extends TestCase { + + private final W32ServiceManager _serviceManager = new W32ServiceManager(); + + @Override + public void setUp() { + _serviceManager.open(Winsvc.SC_MANAGER_CONNECT); + } + + @Override + public void tearDown() { + _serviceManager.close(); + } + + public void testCreateServiceDeleteService() { + // This tests: + // - com.sun.jna.platform.win32.Advapi32.CreateService + // - com.sun.jna.platform.win32.Advapi32.DeleteService + // - com.sun.jna.platform.win32.Advapi32.SERVICE_DESCRIPTION + Win32ServiceDemo.uninstall(); + assertTrue(Win32ServiceDemo.install()); + assertTrue(Win32ServiceDemo.uninstall()); + } + + public void testControlService() { + // Cleanup in case of an unsuccessful previous run + Win32ServiceDemo.uninstall(); + Win32ServiceDemo.install(); + // This test implicitly tests the "service side" functions/members: + // - com.sun.jna.platform.win32.Advapi32.StartServiceCtrlDispatcher + // - com.sun.jna.platform.win32.Advapi32.SERVICE_TABLE_ENTRY + // - com.sun.jna.platform.win32.Advapi32.RegisterServiceCtrlHandlerEx + // - com.sun.jna.platform.win32.Advapi32.SetServiceStatus + // - com.sun.jna.platform.win32.Advapi32.SERVICE_MAIN_FUNCTION + // - com.sun.jna.platform.win32.Advapi32.HandlerEx + // - com.sun.jna.platform.win32.Advapi32.SERVICE_STATUS_HANDLE + W32Service service = _serviceManager.openService(Win32ServiceDemo.serviceName, Winsvc.SERVICE_ALL_ACCESS); + service.startService(); + assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_RUNNING); + service.pauseService(); + assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_PAUSED); + service.continueService(); + assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_RUNNING); + service.stopService(); + assertEquals(service.queryStatus().dwCurrentState, Winsvc.SERVICE_STOPPED); + service.close(); + Win32ServiceDemo.uninstall(); + } + + public void testQueryStatus() { + W32Service service = _serviceManager.openService("eventlog", Winsvc.SERVICE_QUERY_STATUS); + SERVICE_STATUS_PROCESS status = service.queryStatus(); + assertTrue(status.dwCurrentState == Winsvc.SERVICE_RUNNING + || status.dwCurrentState == Winsvc.SERVICE_STOPPED); + service.close(); + } + + public void testSetAndGetFailureActions() { + final String svcId = "w32time"; + final String rebootMsg = "Restarting " + svcId + " due to service failure"; + final String command = "echo " + svcId + " failure"; + final int resetPeriod = 5000; + + W32Service service = _serviceManager.openService(svcId, Winsvc.SC_MANAGER_ALL_ACCESS); + SERVICE_FAILURE_ACTIONS prevActions = service.getFailureActions(); + + List actions = new LinkedList(); + + SC_ACTION action = new SC_ACTION(); + action.type = Winsvc.SC_ACTION_RESTART; + action.delay = 1000; + actions.add(action); + + action = new SC_ACTION(); + action.type = Winsvc.SC_ACTION_REBOOT; + action.delay = 2000; + actions.add(action); + + action = new SC_ACTION(); + action.type = Winsvc.SC_ACTION_RUN_COMMAND; + action.delay = 3000; + actions.add(action); + + action = new SC_ACTION(); + action.type = Winsvc.SC_ACTION_NONE; + action.delay = 4000; + actions.add(action); + + service.setFailureActions(actions, resetPeriod, rebootMsg, command); + + SERVICE_FAILURE_ACTIONS changedActions = service.getFailureActions(); + assertEquals(changedActions.lpRebootMsg, rebootMsg); + assertEquals(changedActions.lpCommand, command); + assertEquals(changedActions.dwResetPeriod, resetPeriod); + assertEquals(changedActions.cActions, 4); + SC_ACTION[] actionArray = (SC_ACTION[]) changedActions.lpsaActions.toArray(changedActions.cActions); + assertEquals(actionArray[0].type, Winsvc.SC_ACTION_RESTART); + assertEquals(actionArray[0].delay, 1000); + assertEquals(actionArray[1].type, Winsvc.SC_ACTION_REBOOT); + assertEquals(actionArray[1].delay, 2000); + assertEquals(actionArray[2].type, Winsvc.SC_ACTION_RUN_COMMAND); + assertEquals(actionArray[2].delay, 3000); + assertEquals(actionArray[3].type, Winsvc.SC_ACTION_NONE); + assertEquals(actionArray[3].delay, 4000); + + // restore old settings + Advapi32.INSTANCE.ChangeServiceConfig2(service._handle, Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS, + prevActions); + + service.close(); + } + + public void testSetFailureActionsFlag() { + W32Service service = _serviceManager.openService("eventlog", Winsvc.SC_MANAGER_ALL_ACCESS); + boolean prevFlag = service.getFailureActionsFlag(); + service.setFailureActionsFlag(!prevFlag); + assertTrue(prevFlag != service.getFailureActionsFlag()); + service.setFailureActionsFlag(prevFlag); + service.close(); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Win32ServiceDemo.java b/contrib/platform/test/com/sun/jna/platform/win32/Win32ServiceDemo.java new file mode 100644 index 0000000000..551ad1f98e --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/Win32ServiceDemo.java @@ -0,0 +1,367 @@ +/* + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Winsvc.HandlerEx; +import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_DESCRIPTION; +import com.sun.jna.platform.win32.Winsvc.SERVICE_MAIN_FUNCTION; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY; +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Baseclass for a Win32 service. + */ +public class Win32ServiceDemo { + + /** + * main. + * + * @param args arguments + */ + public static void main(String[] args) { + Win32ServiceDemo service = new Win32ServiceDemo(); + + if (args.length == 1) { + if (args[0].equalsIgnoreCase("install")) { + System.out.println(Win32ServiceDemo.install()); + } else if (args[0].equalsIgnoreCase("uninstall")) { + System.out.println(Win32ServiceDemo.uninstall()); + } else { + System.out.println("Arguments:"); + System.out.println("install = install service"); + System.out.println("uninstall = uninstall service"); + System.out.println(" = run service"); + System.exit(0); + } + } else { + service.init(); + } + } + + public static final String serviceName = "Win32ServiceDemo"; + public static final String description = "TestService Description"; + private static final Set SUFFIXES = new HashSet(); + + static { + SUFFIXES.add("jna.jar"); + SUFFIXES.add("jna-test.jar"); + SUFFIXES.add("classes"); + } + + private final Object waitObject = new Object(); + private ServiceMain serviceMain; + private ServiceControl serviceControl; + private SERVICE_STATUS_HANDLE serviceStatusHandle; + + public Win32ServiceDemo() { + } + + /** + * Install the service. + * + * @return true on success + */ + public static boolean install() { + boolean success = false; + + // It is assumed, that the ClassLoader loading Win32ServiceDemo for the + // unittest is + // a) an URLClassLoader + // b) holds all relevant dependencies + String invocation; + ClassLoader cl = W32ServiceTest.class.getClassLoader(); + if (cl instanceof URLClassLoader) { + StringBuilder sb = new StringBuilder(); + for (URL u : ((URLClassLoader) cl).getURLs()) { + if ("file".equals(u.getProtocol())) { + try { + File f = new File(u.toURI()); + if (SUFFIXES.contains(f.getName())) { + if (sb.length() != 0) { + sb.append(";"); + } + sb.append(f.getAbsolutePath()); + } + } catch (URISyntaxException ex) { + Logger.getLogger(W32ServiceTest.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + invocation = String.format("java.exe -cp %s com.sun.jna.platform.win32.Win32ServiceDemo", sb.toString()); + System.out.println("Invocation: " + invocation); + } else { + throw new IllegalStateException("Classloader loading Win32ServiceDemo must be an URLClassLoader"); + } + + SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION(); + desc.lpDescription = description; + + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.CreateService( + serviceManager, + serviceName, + serviceName, + Winsvc.SERVICE_ALL_ACCESS, + WinNT.SERVICE_WIN32_OWN_PROCESS, + WinNT.SERVICE_DEMAND_START, + WinNT.SERVICE_ERROR_NORMAL, + invocation, + null, + null, + "\0", + null, + null); + if (service != null) { + success = Advapi32.INSTANCE.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc); + Advapi32.INSTANCE.CloseServiceHandle(service); + } else { + throw new IllegalStateException("Failed to install service "); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + return success; + } + + /** + * Uninstall the service. + * + * @return true on success + */ + public static boolean uninstall() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS); + + if (service != null) { + success = Advapi32.INSTANCE.DeleteService(service); + Advapi32.INSTANCE.CloseServiceHandle(service); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + return success; + } + + /** + * Ask the ServiceControlManager to start the service. + * + * @return true on success + */ + public boolean start() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + + if (service != null) { + success = Advapi32.INSTANCE.StartService(service, 0, null); + Advapi32.INSTANCE.CloseServiceHandle(service); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + + return success; + } + + /** + * Ask the ServiceControlManager to stop the service. + * + * @return true on success + */ + public boolean stop() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + + if (service != null) { + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); + success = Advapi32.INSTANCE.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus); + Advapi32.INSTANCE.CloseServiceHandle(service); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + + return (success); + } + + /** + * Initialize the service, connect to the ServiceControlManager. + */ + public void init() { + serviceMain = new ServiceMain(); + SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY(); + entry.lpServiceName = serviceName; + entry.lpServiceProc = serviceMain; + + Advapi32.INSTANCE.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) 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 static SC_HANDLE openServiceControlManager(String machine, int access) { + return Advapi32.INSTANCE.OpenSCManager(machine, null, access); + } + + /** + * 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) { + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); + serviceStatus.dwServiceType = WinNT.SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwControlsAccepted = status == Winsvc.SERVICE_START_PENDING ? 0 + : (Winsvc.SERVICE_ACCEPT_STOP + | Winsvc.SERVICE_ACCEPT_SHUTDOWN + | Winsvc.SERVICE_CONTROL_PAUSE + | Winsvc.SERVICE_CONTROL_CONTINUE); + serviceStatus.dwWin32ExitCode = win32ExitCode; + serviceStatus.dwWaitHint = waitHint; + serviceStatus.dwCurrentState = status; + + Advapi32.INSTANCE.SetServiceStatus(serviceStatusHandle, serviceStatus); + } + + /** + * Called when service is starting. + */ + public void onStart() { + reportStatus(Winsvc.SERVICE_RUNNING, WinError.NO_ERROR, 0); + } + + /* + * Called when service should stop. + */ + public void onStop() { + reportStatus(Winsvc.SERVICE_STOP_PENDING, WinError.NO_ERROR, 25000); + } + + /* + * Called when service should stop. + */ + public void onPause() { + reportStatus(Winsvc.SERVICE_PAUSED, WinError.NO_ERROR, 0); + } + + /* + * Called when service should stop. + */ + public void onContinue() { + reportStatus(Winsvc.SERVICE_RUNNING, WinError.NO_ERROR, 0); + } + + /** + * Implementation of the service main function. + */ + private class ServiceMain implements 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) { + serviceControl = new ServiceControl(); + serviceStatusHandle = Advapi32.INSTANCE.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null); + + reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 25000); + + 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 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: + onStop(); + synchronized (waitObject) { + waitObject.notifyAll(); + } + break; + case Winsvc.SERVICE_CONTROL_PAUSE: + onPause(); + break; + case Winsvc.SERVICE_CONTROL_CONTINUE: + onContinue(); + break; + } + return WinError.NO_ERROR; + } + } +}