Ačkoliv nám Java nabízí spoustu nástrojů, které můžeme využívat, někdy se přeci jenom musíme “snížit” a použít nativní volání funkce přímo z operačního systému. Jak na to ve Windows si ukážeme v následujícím článku.

Nedávno jsme potřebovali ve své aplikaci zjistit, zda-li je spuštěna aplikace, na které byla druhá aplikace závislá. Bohužel nebylo možné použít kontrolu procesů ve Windows, protože i druhá aplikace běžela pod procesem java.exe, tudíž by nám její jméno bylo k ničemu. Nakonec jsme přišli na řešení, které vyřešilo náš problém – kontrolu existence okna dané aplikace přes Win API a konkrétně metodu FindWindow z user32.dll. Nalézt postup, jak zavolat nativní funkce z této knihovny nám chvíli trvalo a proto se s vámi chceme o tento postup podělit.

Základem je vytvořit následující dvě rozhraní (převzato z této stránky):


public interface W32API extends StdCallLibrary {

    Map UNICODE_OPTIONS = new HashMap() {

        {
            put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
            put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
        }
    };

    Map ASCII_OPTIONS = new HashMap() {

        {
            put(OPTION_TYPE_MAPPER, W32APITypeMapper.ASCII);
            put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.ASCII);
        }
    };


    Map DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS;

    public static class HANDLE extends PointerType {

        public Object fromNative(Object nativeValue, FromNativeContext context) {
            Object o = super.fromNative(nativeValue, context);
            if (INVALID_HANDLE_VALUE.equals(o))
                return INVALID_HANDLE_VALUE;
            return o;
        }
    }

    public static class HWND extends HANDLE {}

    HANDLE INVALID_HANDLE_VALUE = new HANDLE() {

        {
            super.setPointer(Pointer.createConstant(-1));
        }

        public void setPointer(Pointer p) {
            throw new UnsupportedOperationException("Immutable reference");
        }
    };
}

public interface USER32 extends W32API {

    USER32 INSTANCE = (USER32)Native.loadLibrary("user32", USER32.class, DEFAULT_OPTIONS);

    boolean ShowWindow(HWND hWnd, int nCmdShow);
    boolean SetForegroundWindow(HWND hWnd);
    HWND FindWindow(String winClass, String title);
}

Tato dvě rozhraní se starají o zprostředkování přístupu k nativním knihovnám Windows (v našem případě k user32.dll). User32 je známá knihovna, která umožňuje práci především s GUI Windows a disponuje mnoha zajímavými funkcemi (viz například tento seznam). Mezi ně patří i námi požadovaná funkce FindWindow, která najde okno dle jeho názvu a vrátí nám hWND. hWND je “window handler” a jedná se o standardní způsob jak můžou cizí aplikace dostat k danému oknu. Jako parametry má pak winClass což je skupina oken, do kterých hledané okno spadá (např. Shell_TrayWnd – vezme okna pouze z Windows tray apod. – pokud uvedeme null, pak se hledá všude, nezávisle na třídě) a title což je titulek okna.

Další zmíněné metody pak slouží k přenesení okna do popředí (SetForegroundWindow) a k nastavení stavu ona (ShowWindow – více informací zde). Obě metody mají v parametrech hWND, který získáme právě z metody FindWindow (jsou samozřejmě i jiné cesty).

Pokud tedy chceme nalézt okno, které se jmenuje Eclipse, poté nám stačí zavolat metodu z našeho rozhraní:


public boolean isEclipseRunning() {
    W32API.HWND hwnd = USER32.INSTANCE.FindWindow(null, "Eclipse");
    return hwnd != null;
}


Tímto jednoduchým způsobem můžeme ověřit, že daná aplikace je již spuštěna (bohužel nám to neřekne nic o jejím stavu) a reagovat na to v naší aplikaci.

Vybráno z komentářů

JNA?

Dobrý den,

pochopil jsem to dobře, že zde mluvíte o použití knihovny JNA?

Nikde to v celém článku není uvedeno.

Odpověď na JNA

Ano, opravdu se jedná o použití knihovny JNA – omlouvám se, že jsem na to blíže nepoukázal.