MacOS에서 Java (JNA 사용)에서 전경 창 / 프로세스를 얻는 방법은 무엇입니까?

Aug 18 2020

현재 MS Windows에서 전경 (상단) 창 / 프로세스를 얻기 위해 노력하고 있습니다. JNA를 사용하여 macOS에서 비슷한 작업을 수행해야합니다.

macOS의 해당 코드는 무엇입니까?

  byte[] windowText = new byte[512];
  PointerType hwnd = User32.INSTANCE.GetForegroundWindow();  
  User32.INSTANCE.GetWindowTextA(hwnd, windowText, 512);
  System.out.println(Native.toString(windowText));  

답변

2 DanielWiddis Aug 18 2020 at 22:53

실제로 여기에는 전경 창과 전경 프로세스라는 두 가지 질문이 있습니다. 나는 둘 다 대답하려고 노력할 것입니다.


포 그라운드 프로세스의 경우 JNA를 사용하는 쉬운 방법은 Application Services API 를 매핑하는 것 입니다. 이러한 함수는 10.9에서 도입되었으며 현재 더 이상 사용되지 않지만 10.15부터 계속 작동합니다. 최신 버전은에 AppKitLibrary있습니다. 아래를 참조하세요.

이 클래스를 만들고 필요한 두 함수를 매핑합니다.

public interface ApplicationServices extends Library {
    ApplicationServices INSTANCE = Native.load("ApplicationServices", ApplicationServices.class);

    int GetFrontProcess(LongByReference processSerialNumber);
    int GetProcessPID(LongByReference processSerialNumber, IntByReference pid);
}

"전경"프로세스는를 사용하여 얻을 수 있습니다 GetFrontProcess(). 그러면 ProcessSerialNumberApplication Services API 전체에서 사용되는 고유 한 64 비트 값인 이라는 것이 반환 됩니다. 사용자 공간 사용을 위해 번역하려면 프로세스 ID가 필요할 수 있으며 GetProcessPID()해당 번역을 수행합니다.

LongByReference psn = new LongByReference();
IntByReference pid = new IntByReference();
ApplicationServices.INSTANCE.GetFrontProcess(psn);
ApplicationServices.INSTANCE.GetProcessPID(psn, pid);
System.out.println("Front process pid: " + pid.getValue());

위의 작업은 작동하지만 더 이상 사용되지 않습니다. 새 응용 프로그램은 다음을 사용해야합니다 AppKitLibrary.

public interface AppKitLibrary extends Library {
    AppKitLibrary INSTANCE = Native.load("AppKitLibrary", AppKitLibrary.class);
}

등이 라이브러리를 사용하여 맨 위의 응용 프로그램에 대한 여러 다른 StackOverflow의 질문이 있습니다 이 하나 . 필요한 모든 가져 오기 및 개체를 매핑하는 것은 여기에 답변을 제공하는 것보다 훨씬 더 많은 작업이지만 유용 할 수 있습니다. 이 API에 액세스 하기 위해 Rococoa 프레임 워크 (내부에서 JNA를 사용하지만 이미 JNAerator를 통해 모든 AppKit를 매핑) 를 사용하는 방법을 알아내는 것이 더 쉬울 것입니다 . 일부 javadocs는 여기에 있습니다 .

도 있습니다 솔루션을 사용 AppleScript하면 명령 줄 사용을 통해 자바에서 실행할 수있는 Runtime.exec()출력을 캡처가.


화면의 전경 창과 관련하여 조금 더 복잡합니다. 에 내 대답은 맥 OS에서 모든 창을 반복에 당신의 이전 질문에, 내가 사용하는 모든 윈도우의 목록을 얻는 방법을 대답 CoreGraphics포함, JNA를 통해 CFDictionary포함 자세한 내용은.

이러한 사전 키 중 하나는 창 레이어 번호를 나타내는를 kCGWindowLayer반환합니다 CFNumber. 문서에는 이것이 32 비트이므로 intValue()적절하다고 명시되어 있습니다. 숫자는 "그리기 순서"이므로 높은 숫자는 낮은 숫자를 덮어 씁니다. 따라서 검색된 모든 창을 반복하고 최대 수를 찾을 수 있습니다. 이것이 "전경"레이어가됩니다.

몇 가지주의 사항이 있습니다.

  • 실제로 사용할 수있는 레이어는 20 개뿐입니다. 많은 것들이 레이어를 공유합니다.
  • 레이어 1000은 화면 보호기입니다. 1000 이상의 레이어는 무시할 수 있습니다.
  • 레이어 24는 일반적으로 맨 위에있는 Dock이며 상위 레벨에있는 레이어 25 (도크의 아이콘)입니다.
  • 레이어 0은 데스크탑의 나머지 부분으로 보입니다.
  • 어떤 창이 "위에"있는지는 화면에서 보는 위치에 따라 다릅니다. 도크 위에 도크가 전경 (또는 응용 프로그램 아이콘)에 있습니다. 나머지 화면에서는 평가중인 픽셀과 CoreGraphics 창에서 얻은 화면 사각형을 확인해야합니다. (( 4 doubles, X, Y, width, height가있는 구조) kCGWindowBounds를 반환하는 키를 사용하십시오 CGRect.

화면 창으로 필터링해야합니다. 이미 목록을 가져온 경우 kCGWindowIsOnscreen키를 사용하여 창이 표시되는지 여부를 결정할 수 있습니다. 그것은을 반환합니다 CFBoolean. 이 키는 선택 사항이므로 null. 그러나 아무것도없는 상태에서 시작하는 경우 처음을 호출 할 때 kCGWindowListOptionOnScreenOnly 창 옵션 상수 를 사용하는 것이 좋습니다 CGWindowListCopyWindowInfo().

모든 창 을 반복하는 것 외에도이 CGWindowListCopyWindowInfo()함수는 CGWindowID매개 변수를 취하고 옵션에 relativeToWindow비트 단위 또는 사용하여 추가 할 수 kCGWindowListOptionOnScreenAboveWindow있습니다.

마지막으로 현재 세션과 관련된 창으로 제한하는 것이 유용 할 수 있으며 CGWindowListCreate()유사한 구문을 사용하여 CopyInfo()변형에 매핑해야합니다 . 사전 검색을 제한 할 수있는 창 번호 배열을 반환하거나 해당 배열을에 인수로 전달할 수 CGWindowListCreateDescriptionFromArray()있습니다.

이전 답변에서 언급했듯이 사용자는 Create또는 Copy함수 를 사용하여 생성 한 모든 개체를 "소유" 하고, 메모리 누수를 방지하기 위해 작업이 완료되면이를 해제 할 책임이 있습니다.

BinLiu Oct 18 2020 at 18:31
   AppleScriptEngine appleEngine = new apple.applescript.AppleScriptEngine();
    ArrayList<String> processNames = null;
    try {
        String processName = null;
        processNames = (ArrayList<String>) appleEngine
                .eval("tell application \"System Events\" to get name of application processes whose frontmost is true and visible is true");
        if (processNames.size() > 0) {
            processName = processNames.get(0);// the front most process name
        }
        return processName;
    } catch (ScriptException e) {
        log.debug("no app running");
    }