JNA Windows Service Startup Type

梦想与她 提交于 2019-12-06 11:26:45

The problem here is that while the JNA platform-specific code provides handling for querying a service's status, it does not provide support for querying the service's configuration. That means that to do so, you'll need to provide a JNA mapping for the function in question.

The function you'd want, in this case, is QueryServiceConfig() defined in Advapi32. This function fills in a QUERY_SERVICE_CONFIG structure, which has a dwStartType property which corresponds to the various start-up type values.

Fortunately, mapping a native function is really straight-forward with JNA: you just declare an interface like so (The code examples I'm providing are written in Groovy; the transformation to Java should be pretty straight-forward):

interface MyAdvapi32 extends StdCallLibrary {
    MyAdvapi32 INSTANCE = (Advapi32) Native.loadLibrary("Advapi32", MyAdvapi32.class, W32APIOptions.UNICODE_OPTIONS);

    boolean QueryServiceConfig(
        SC_HANDLE hService,
        QUERY_SERVICE_CONFIG lpServiceConfig,
        int cbBufSize,
        IntByReference pcbBytesNeeded
    )
}

(I derived this using the definition for QueryServiceStatusEx() in the JNA source, whose parameters closely mirror the parameters for QueryServiceConfig(). Note that QueryServiceStatusEx() is ultimately the function called by W32Service#queryStatus()).

However, our function requires a QUERY_SERVICE_CONFIG structure, which is not defined anywhere in JNA. Working from the model of the JNA's SERVICE_STATUS_PROCESS definition we end up with something like:

class QUERY_SERVICE_CONFIG extends Structure {
    public DWORD dwServiceType
    public DWORD dwStartType
    public DWORD dwErrorControl
    public char[] lpBinaryPathName
    public char[] lpLoadOrderGroup
    public DWORD dwTagId
    public char[] lpDependencies
    public char[] lpServiceStartName
    public char[] lpDisplayName

    QUERY_SERVICE_CONFIG() {}
    QUERY_SERVICE_CONFIG(int size) {
        lpBinaryPathName   = new char[256]
        lpLoadOrderGroup   = new char[256]
        lpDependencies     = new char[256]
        lpServiceStartName = new char[256]
        lpDisplayName      = new char[256]

        allocateMemory(size)
    }
}

(Note that this structure is quite a bit more involved than SERVICE_STATUS_PROCESS, as SERVICE_STATUS_PROCESS only has DWORD parameters. The allocation sizes I provided in the second constructor, while required for JNA, are probably not the right size.)

Armed with our structure and new interface, we can create a method to call QueryServiceConfig:

QUERY_SERVICE_CONFIG queryServiceConfig(W32Service service) {
    IntByReference size = new IntByReference()

    MyAdvapi32.INSTANCE.QueryServiceConfig(
        service.handle,
        null,
        0,
        size
    )

    QUERY_SERVICE_CONFIG config = new QUERY_SERVICE_CONFIG(size.value)

    if (!MyAdvapi32.INSTANCE.QueryServiceConfig(
        service.handle,
        config,
        config.size(),
        size
    )) {
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }

    return config
}

Once we've got all that, using it is pretty simple:

QUERY_SERVICE_CONFIG config = queryServiceConfig(service)
System.out.println(config.dwStartType)

Using JNA 4.2.2:

The problem I was having with this structure was the lpDependencies defined as a doubly null terminated array of strings:

lpDependencies

A pointer to an array of null-separated names of services or load ordering groups that must start before this service. The array is doubly null-terminated .

So I solved this issue by customizing the type mapping:

public class QUERY_SERVICE_CONFIG extends Structure
    {

        public QUERY_SERVICE_CONFIG(Pointer p)
        {
            super(p, ALIGN_DEFAULT, new MyTypeMapper());
        }

        public int dwServiceType;
        public int dwStartType;
        public int dwErrorControl;
        public String lpBinaryPathName;
        public String lpLoadOrderGroup;
        public int dwTagId;
        public TypeMappers.DoubleNullString lpDependencies;
        public String lpServiceStartName;
        public String lpDisplayName;

       @Override
        protected List<String> getFieldOrder()
        {
            return Arrays
                    .asList("dwServiceType", "dwStartType", "dwErrorControl", "lpBinaryPathName", "lpLoadOrderGroup",
                        "dwTagId", "lpDependencies", "lpServiceStartName", "lpDisplayName");
        }

        public static class MyTypeMapper implements TypeMapper
        {

            @Override
            public FromNativeConverter getFromNativeConverter(Class javaType)
            {
                FromNativeConverter result = null;
                if (javaType.equals(TypeMappers.DoubleNullString.class))
                {
                    result = TypeMappers.DoubleNullString.FROM_NATIVE_CONVERTER;
                }
                return result;
            }

            @Override
            public ToNativeConverter getToNativeConverter(Class javaType)
            {
                ToNativeConverter result = null;
                if (javaType.equals(TypeMappers.DoubleNullString.class))
                {
                    result = TypeMappers.DoubleNullString.TO_NATIVE_CONVERTER;
                }
                return result;
            }
        }

    }

I really only cared for the "FromNativeConverter" (DoubleNullString is just a Type marker that has a String[] field):

public class MyFromNativeConverter implements FromNativeConverter
{
    @Override
    public Object fromNative(Object nativeValue, FromNativeContext context)
    {
        DoubleNullString result = new DoubleNullString();
        Pointer p = (Pointer) nativeValue;
        int offset = 0;
        List<String> doubleNullList = new ArrayList<>();
        while (!(p.getByte(offset) == 0))
        {
            String s = p.getString(offset);
            doubleNullList.add(s);
            offset += s.length() + 1;
        }
        result.lpDependencies = doubleNullList.toArray(new String[doubleNullList.size()]);
        return result;
    }

    @Override
    public Class nativeType()
    {
        return Pointer.class;
    }
}

For reference the ToNativeConverter should just return a Pointer.class type and if needed, could return a Memory block with the String[] converted to a doubly null terminated array of bytes. For JNA's sake, it just needs to know the "type" and a default "null" value (just so it can initialize the structure).

My method signature then becomes:

Memory memory = new Memory(required.getValue());
Advapi32Ex.QUERY_SERVICE_CONFIG query = new Advapi32Ex.QUERY_SERVICE_CONFIG(memory);
Advapi32Ex.INSTANCE.QueryServiceConfigA(session.getHandle(), query, (int) memory.size(), required);

Here is the program from the previous answer as a java class. IMPORTANT - needs exactly JNA 3.3.0

import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

import java.util.Arrays;
import java.util.List;

public class ServiceUtil {

    interface MyAdvapi32 extends StdCallLibrary {
        public MyAdvapi32 INSTANCE = (MyAdvapi32) Native.loadLibrary("Advapi32", MyAdvapi32.class, W32APIOptions.UNICODE_OPTIONS);
        public boolean QueryServiceConfig(
                Winsvc.SC_HANDLE hService,
                QUERY_SERVICE_CONFIG lpServiceConfig,
                int cbBufSize,
                IntByReference pcbBytesNeeded
        );
    }

    public static class QUERY_SERVICE_CONFIG extends Structure {
        public WinDef.DWORD dwServiceType;
        public WinDef.DWORD dwStartType;
        public WinDef.DWORD dwErrorControl;
        public char[] lpBinaryPathName;
        public char[] lpLoadOrderGroup;
        public WinDef.DWORD dwTagId;
        public char[] lpDependencies;
        public char[] lpServiceStartName;
        public char[] lpDisplayName;

        public QUERY_SERVICE_CONFIG() {}
        public QUERY_SERVICE_CONFIG(int size) {
            lpBinaryPathName = new char[256];
            lpLoadOrderGroup = new char[256];
            lpDependencies = new char[256];
            lpServiceStartName = new char[256];
            lpDisplayName = new char[256];
            allocateMemory(size);
        }

        @Override
        protected List getFieldOrder() {
            return Arrays.asList("lpBinaryPathName","lpLoadOrderGroup","lpDependencies","lpServiceStartName","lpDisplayName");
        }
    }
    public static QUERY_SERVICE_CONFIG queryServiceConfig(W32Service service) {
        IntByReference size = new IntByReference();
        MyAdvapi32.INSTANCE.QueryServiceConfig(
                service.getHandle(),
                null,
                0,
                size
        );
        QUERY_SERVICE_CONFIG config = new QUERY_SERVICE_CONFIG(size.getValue());
        if (!MyAdvapi32.INSTANCE.QueryServiceConfig(
                service.getHandle(),
                config,
                config.size(),
                size
        )) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }
        return config;
    }



    public static String checkService(String serviceToCheck) {
        W32ServiceManager serviceManager = new W32ServiceManager();
        serviceManager.open(Winsvc.SC_MANAGER_ALL_ACCESS);
        W32Service service = serviceManager.openService(serviceToCheck, Winsvc.SC_MANAGER_ALL_ACCESS);

        IntByReference size = new IntByReference();
        MyAdvapi32.INSTANCE.QueryServiceConfig(
                service.getHandle(),
                null,
                0,
                size
        );


        QUERY_SERVICE_CONFIG config = queryServiceConfig(service);
        String result = config.dwStartType.toString();
        service.close();
        return result;

    }



}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!