Skip to content

Commit

Permalink
Update W32Service#stopService to be more resilent to delayed status u…
Browse files Browse the repository at this point in the history
…pdates

If the service asked to be stopped does not immediatly update its state
in response to a stop command, the stopService method raised an exception.

This change follows a sample on MSDN:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686335(v=vs.85).aspx

It only uses the status query to determine the final state and not
to see if the services reacted to the stop request.

Closes: java-native-access#797
  • Loading branch information
matthiasblaesing committed Mar 18, 2018
1 parent 6e051fb commit f448a63
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 5 deletions.
19 changes: 19 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,25 @@ boolean QueryServiceStatusEx(SC_HANDLE hService, int InfoLevel,
SERVICE_STATUS_PROCESS lpBuffer, int cbBufSize,
IntByReference pcbBytesNeeded);

/**
* Retrieves the current status of the specified service based on the
* specified information level.
*
* @param hService
* A handle to the service. This handle is returned by the
* OpenService(SC_HANDLE, String, int) or CreateService()
* function, and it must have the SERVICE_QUERY_STATUS access
* right. For more information, see <a
* href="http://msdn.microsoft.com/en-us/library/ms685981.aspx"
* >Service Security and Access Rights</a>.
* @param lpServiceStatus
* A pointer to a SERVICE_STATUS structure that receives the status information.
* @return If the function succeeds, the return value is true. If the
* function fails, the return value is false. To get extended error
* information, call GetLastError.
*/
boolean QueryServiceStatus(SC_HANDLE hService, SERVICE_STATUS lpServiceStatus);

/**
* Sends a control code to a service. To specify additional information when
* stopping a service, use the ControlServiceEx function.
Expand Down
39 changes: 34 additions & 5 deletions contrib/platform/src/com/sun/jna/platform/win32/W32Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
import com.sun.jna.platform.win32.Winsvc.SC_ACTION;
import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
import com.sun.jna.platform.win32.Winsvc.SC_STATUS_TYPE;
import static com.sun.jna.platform.win32.Winsvc.SERVICE_CONTROL_STOP;
import com.sun.jna.platform.win32.Winsvc.SERVICE_FAILURE_ACTIONS;
import com.sun.jna.platform.win32.Winsvc.SERVICE_FAILURE_ACTIONS_FLAG;
import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS;
import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS;
import com.sun.jna.ptr.IntByReference;

Expand Down Expand Up @@ -204,19 +206,46 @@ public void startService() {
}
}

/**
* Stop service.
*/
public void stopService() {
stopService(30000);
}

/**
* Stop service.
*/
public void stopService() {
*
* @param timeout timeout in ms until the service must report to be
* stopped
*/
public void stopService(long timeout) {
long startTime = System.currentTimeMillis();
waitForNonPendingState();
// If the service is already stopped - return
if (queryStatus().dwCurrentState == Winsvc.SERVICE_STOPPED) {
return;
}
if (! Advapi32.INSTANCE.ControlService(_handle, Winsvc.SERVICE_CONTROL_STOP,
new Winsvc.SERVICE_STATUS())) {
SERVICE_STATUS status = new SERVICE_STATUS();
if (! Advapi32.INSTANCE.ControlService(_handle, SERVICE_CONTROL_STOP, status)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
// This following the sample from the MSDN
// the previouos implementation queried the service status and
// failed if the application did not correctly update its state
while(status.dwCurrentState != Winsvc.SERVICE_STOPPED) {
try {
Thread.sleep( status.dwWaitHint );
} catch (InterruptedException e){
throw new RuntimeException(e);
}
if(! Advapi32.INSTANCE.QueryServiceStatus(_handle, status)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
if((System.currentTimeMillis() - startTime) > timeout) {
throw new RuntimeException(String.format("Service stop exceeded timeout time of %d ms", timeout));
}
}
waitForNonPendingState();
if (queryStatus().dwCurrentState != Winsvc.SERVICE_STOPPED) {
throw new RuntimeException("Unable to stop the service");
Expand Down Expand Up @@ -269,7 +298,7 @@ public void waitForNonPendingState() {
SERVICE_STATUS_PROCESS status = queryStatus();

int previousCheckPoint = status.dwCheckPoint;
int checkpointStartTickCount = Kernel32.INSTANCE.GetTickCount();;
int checkpointStartTickCount = Kernel32.INSTANCE.GetTickCount();

while (isPendingState(status.dwCurrentState)) {

Expand Down

0 comments on commit f448a63

Please sign in to comment.