Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with W32Service.stopService() #797

Closed
googol4u opened this issue Apr 27, 2017 · 9 comments
Closed

Issue with W32Service.stopService() #797

googol4u opened this issue Apr 27, 2017 · 9 comments

Comments

@googol4u
Copy link

Thanks for develop such a great tool for java native bindings.

However, I have found a issue in W32Service.stopService(). In my scenario, when I use W32Service.stopService() to stop one of my windows services, it throws Unable to stop service exception. Digging into the issue, I find that in my scenario, When Advapi32.INSTANCE.ControlService(handle, Winsvc.SERVICE_CONTROL_STOP, new Winsvc.SERVICE_STATUS()) is called, the dwCurrentState of the service is not changed to SERVICE_STOP_PENDING but still is SERVICE_RUNNING. So, the waitForNonPendingState() will return immediately and queryStatus().dwCurrentState != Winsvc.SERVICE_STOPPED will be true.
Therefore, the Unable to stop service excepiton will be throwed.

My System is Windows 8.1.

  1. Version of JNA and related jars: 4.4.0
  2. oracle jdk 1.8.0_121
  3. Windows 8.1
  4. Intel64 Family 6 Model 61 Stepping 4 GenuineIntel ~1601 Mhz, JVM 32bit
  5. Complete description of the problem(see the above)
  6. Steps to reproduce ( see the above )
@matthiasblaesing
Copy link
Member

What do you suggest as "correct" behaviour? While the implementation could be criticized (throwing RuntimeException instead of something more specific), the implementation seems to be in line with the description (MSDN):

The SCM processes service control notifications in a serial fashion—it will wait for one service
to complete processing a service control notification before sending the next one. Because
of this, a call to ControlService will block for 30 seconds if any service is busy handling a
control code. If the busy service still has not returned from its handler function when the
timeout expires, ControlService fails with ERROR_SERVICE_REQUEST_TIMEOUT.

My reading: The service must react to the request within 30s. When the request returns the service should have aknowledged the request and so STOP_PENDING would be the correct state.

@googol4u
Copy link
Author

In my scenario, the service could be stopped in a very short period. The problem is that the dwCurrentState does not change to SERVICE_STOP_PENDING. So instead of wait for the service to stop within 30s, the method throw exception immediately and the service actually could be stopped.

@matthiasblaesing
Copy link
Member

You did not address the question whether the service might do the wrong thing. Anyway, I'm open to reviewing a PR that suggest a better implementation.

@googol4u
Copy link
Author

Is the change of dwCurrentState to SERVICE_STOP_PENDING done by the service or by the os?

@googol4u
Copy link
Author

@matthiasblaesing
Copy link
Member

What does the SO post answer? I still say that the correct state after requesting STOP is STOP_PENDING or STOPPED, not RUNNING. In your case this is not true and it fails.

We are back at the start: The service just might have a broken service routine.

@googol4u
Copy link
Author

googol4u commented May 3, 2017

I am using the Java Service Wrapper ( https://wrapper.tanukisoftware.com/ )to wrap one of my java app into a windows service.

I have written a test:

package test;

import com.sun.jna.platform.win32.Advapi32;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.W32Service;
import com.sun.jna.platform.win32.W32ServiceManager;
import com.sun.jna.platform.win32.Winsvc;
import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS;

public class TestJna {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
        try
        {
            W32ServiceManager serviceManager = new W32ServiceManager();
            serviceManager.open(Winsvc.SC_MANAGER_ALL_ACCESS); 
            W32Service service = serviceManager.openService("testwrapper", Winsvc.SC_MANAGER_ALL_ACCESS);
            System.err.println("stopping...");
            SC_HANDLE handle = service.getHandle();
            Advapi32.INSTANCE.ControlService(handle, Winsvc.SERVICE_CONTROL_STOP,new Winsvc.SERVICE_STATUS());
            SERVICE_STATUS_PROCESS status;
            do {
            	status = service.queryStatus();
            	System.err.println("GetTickCount:"+Kernel32.INSTANCE.GetTickCount()+
            			",dwWaitHint="+status.dwWaitHint+
            			",dwCheckPoint="+status.dwCheckPoint+
            			",dwCurrentState="+status.dwCurrentState);
            } while (status.dwCurrentState!=Winsvc.SERVICE_STOPPED);
            
            System.err.println("stopped!");
            service.close();
        } catch (Exception ex)
        {
            ex.printStackTrace();
        }

	}

}

and the result is like the following:

stopping...
GetTickCount:69486921,dwWaitHint=0,dwCheckPoint=0,dwCurrentState=4
GetTickCount:69486921,dwWaitHint=30000,dwCheckPoint=16,dwCurrentState=3
GetTickCount:69486921,dwWaitHint=30000,dwCheckPoint=16,dwCurrentState=3
GetTickCount:69486921,dwWaitHint=30000,dwCheckPoint=16,dwCurrentState=3
...
GetTickCount:69487640,dwWaitHint=30000,dwCheckPoint=22,dwCurrentState=3
GetTickCount:69487640,dwWaitHint=0,dwCheckPoint=0,dwCurrentState=3
GetTickCount:69487640,dwWaitHint=0,dwCheckPoint=0,dwCurrentState=1
stopped!

Seems that Java Service Wrapper handle the state correctly (correct dwWaiHint, increasing dwCheckPoint as progressing). The only problem is that in a very short period after issuing the SERVICE_CONTROL_STOP, the dwCurrentState will be RUNNING (4) intead of STOP_PENDING (3).

@matthiasblaesing
Copy link
Member

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

And taking into account, that QueryServiceStatusEx remarks (https://msdn.microsoft.com/de-de/library/windows/desktop/ms684941(v=vs.85).aspx):

Remarks

The QueryServiceStatusEx function returns the most recent service status information reported
to the service control manager. If the service just changed its status, it may not have updated
the service control manager yet.

In my opinion, the implementation should be adjusted.

@googol4u you could try to reimplement the stopService method according to MSDN and make that a PR

matthiasblaesing added a commit to matthiasblaesing/jna that referenced this issue Mar 18, 2018
…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
@matthiasblaesing
Copy link
Member

@googol4u please see if #942 helps for your case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants