简体   繁体   English

JNA Windows服务启动类型

[英]JNA Windows Service Startup Type

I've been playing around with JNA and am able to return the status of a Windows Service (ie started or stopped) using the code below. 我一直在使用JNA,并且能够使用以下代码返回Windows服务的状态(即启动或停止)。 I'm not sure how to return the startup type of the service though. 我不确定如何返回服务的启动类型。 I'm sure there are ways outside of JNA, but I would like to continue to use JNA if possible. 我确定JNA之外还有其他方法,但我想尽可能继续使用JNA。

import com.sun.jna.*;
import com.sun.jna.Library.Handler;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.Advapi32Util.*;
import com.sun.jna.platform.win32.WinNT.*;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.*;


public class WindowsService {

public static void main(String[] args) {

    W32ServiceManager serviceManager = new W32ServiceManager();             
    serviceManager.open(Winsvc.SC_MANAGER_ALL_ACCESS);              
    W32Service service = serviceManager.openService("W32Time", Winsvc.SC_MANAGER_ALL_ACCESS);             
    System.out.println(service.queryStatus().dwCurrentState);
    service.close();

     }
}

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. 这里的问题是,尽管特定于JNA平台的代码提供了查询服务状态的处理,但它不提供查询服务配置的支持。 That means that to do so, you'll need to provide a JNA mapping for the function in question. 这意味着,您需要为所涉及的功能提供JNA映射。

The function you'd want, in this case, is QueryServiceConfig() defined in Advapi32. 在这种情况下,您想要的功能是Advapi32中定义的QueryServiceConfig() This function fills in a QUERY_SERVICE_CONFIG structure, which has a dwStartType property which corresponds to the various start-up type values. 此函数填充QUERY_SERVICE_CONFIG结构,该结构具有dwStartType属性,该属性对应于各种启动类型值。

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): 幸运的是,使用JNA映射本机函数确实很简单:您只需声明一个接口即可(我提供的代码示例是用Groovy编写的;到Java的转换应该很简单):

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() ). (我使用JNA源代码中QueryServiceStatusEx()定义派生了该参数,其参数与QueryServiceConfig()的参数非常QueryServiceConfig() 。请注意, QueryServiceStatusEx()最终是W32Service#queryStatus()调用的函数)。

However, our function requires a QUERY_SERVICE_CONFIG structure, which is not defined anywhere in JNA. 但是,我们的函数需要QUERY_SERVICE_CONFIG结构,该结构未在JNA中定义。 Working from the model of the JNA's SERVICE_STATUS_PROCESS definition we end up with something like: 从JNA的SERVICE_STATUS_PROCESS定义模型开始,我们最终得到如下结果:

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.) (请注意,此结构比SERVICE_STATUS_PROCESS涉及得多,因为SERVICE_STATUS_PROCESS仅具有DWORD参数。我在第二个构造函数中提供的分配大小(对于JNA是必需的)可能不是正确的大小。)

Armed with our structure and new interface, we can create a method to call QueryServiceConfig: 有了我们的结构和新接口,我们可以创建一个方法来调用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: 使用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定义为以双终止符终止的字符串数组:

lpDependencies 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 . 数组是双重以null结尾的

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): 我真的只关心“ FromNativeConverter”(DoubleNullString只是具有String []字段的Type标记):

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. 作为参考, ToNativeConverter应该只返回Pointer.class类型,并且如果需要,可以返回将String []转换为双倍终止的字节数组的Memory块。 For JNA's sake, it just needs to know the "type" and a default "null" value (just so it can initialize the structure). 为了JNA的缘故,它只需要知道“ type”和默认的“ null”值(恰好可以初始化结构)。

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. 这是来自上一个答案的程序作为Java类。 IMPORTANT - needs exactly JNA 3.3.0 重要-完全需要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;

    }



}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM