java - JNA Windows Service Startup Type

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har spillet rundt med JNA og er i stand til at returnere status for en Windows Service (dvs. startet eller stoppet) ved hjælp af koden nedenfor. Jeg er ikke sikker på, hvordan jeg skal returnere starttypen af ​​tjenesten. Jeg er sikker på, at der findes måder uden for JNA, men jeg vil gerne fortsætte med at bruge JNA, hvis det er muligt.


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();

     }
}

Bedste reference


Problemet her er, at mens den JNA-platformspecifikke kode giver håndtering til forespørgsel af en tjeneste status, giver den ikke støtte til at spørge tjenesteens konfiguration. Det betyder at for at gøre det, skal du give en JNA-kortlægning til den pågældende funktion.


Den funktion du vil have i dette tilfælde er QueryServiceConfig() defineret i Advapi32. Denne funktion udfylder en QUERY\_SERVICE\_CONFIG struktur, som har en dwStartType egenskab, som svarer til de forskellige opstart type værdier. [22] [23]


Heldigvis er kortlægning af en indbygget funktion virkelig lige fremad med JNA: Du erklærer bare en grænseflade som sådan (Kodeksemplerne jeg giver er skrevet i Groovy; omdannelsen til Java skal være ret ligefrem):


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
    )
}


(Jeg har afledt dette ved at bruge definitionen for QueryServiceStatusEx() i JNA-kilden, hvis parametre tæt afspejler parametrene for QueryServiceConfig(). Bemærk at QueryServiceStatusEx() i sidste ende er den funktion, der kaldes af W32Service#queryStatus()) . [24] [25]


Men vores funktion kræver en QUERY\_SERVICE\_CONFIG struktur, som ikke er defineret nogen steder i JNA. At arbejde ud fra modellen af ​​JNAs SERVICE\_STATUS\_PROCESS definition slutter vi med noget som: [26]


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)
    }
}


(Bemærk at denne struktur er ret mere involveret end SERVICE\_STATUS\_PROCESS, da SERVICE\_STATUS\_PROCESS kun har DWORD parametre. De tildelingsstørrelser, jeg leverede i den anden konstruktor, mens det kræves for JNA, er sandsynligvis ikke den rigtige størrelse.) [27]


Bevæbnet med vores struktur og ny grænseflade, kan vi oprette en metode til at ringe til 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
}


Når vi har fået det hele, er det ret simpelt at bruge det:


QUERY\_SERVICE\_CONFIG config = queryServiceConfig(service)
System.out.println(config.dwStartType)

Andre referencer 1


Brug af JNA 4.2.2:


Problemet jeg havde med denne struktur var lpDependencies defineret som en dobbelt null termineret række af strenge:



  

lpDependencies


  
  En peger på en række nul-adskilte navne på tjenester eller last ordre grupper, der skal starte før denne service. Feltet er dobbelt nullstillet .



Så jeg løste dette problem ved at tilpasse typen kortlægning:


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;
            }
        }

    }


Jeg plejede virkelig kun 'FromNativeConverter' (DoubleNullString er bare en Type markør, der har et String [[]] -felt):


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;
    }
}


Til reference bør ToNativeConverter bare returnere en Pointer.class-type og kunne om nødvendigt returnere en hukommelsesblok med strengen [[]] konverteret til en dobbelt null termineret række bytes. For JNA skyldes det bare at kende 'type' og en standard 'null' værdi (bare så det kan initialisere strukturen).


Min metode underskrift bliver så:


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);

Andre referencer 2


Her er programmet fra det foregående svar som en java-klasse.
VIGTIGT - har brug for præcis 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;

    }



}