2013-10-09 1 views
2

더 큰 프로젝트에 응용 프로그램 스위처를 추가하려고합니다. 그것은 Windows XP/Vista/7/8에서 작동해야합니다. Java 1.7을 사용하고 있습니다. 다음은 내가 겪고있는 문제 중 일부를 보여주기 위해 작성한 샘플 애플리케이션입니다. 나는 JNA에게 아주 새로운 사람입니다.Java - JNA를 사용하는 Windows 작업 표시 줄 - 어떻게 창 아이콘 (HICON)을 Java 이미지로 변환합니까?

테스트 응용 프로그램의 기초가되는 this answer (및 기타 다수!)의 'Hovercraft Full of Eels'에 감사드립니다. 여기

내 질문은 다음과 같습니다

  1. 이미지 그리기 - 나는 창 아이콘에서 얻을 이미지는 흑백으로 그려지고있다. 나는 맥도웰에 의해 this answer에서 getImageForWindow의 코드를 수정했다. (고마워!). HICON 객체를 java.awt.Image로 변환하는 더 좋은 방법이 있습니까? com.sun.jna.platform.win32.W32API.HICON에서 'fromNative'라는 메서드가 있음을 알았지 만 사용 방법을 알 수 없습니다. 아이콘 얻기

  2. - 나는 아이콘 핸들을 얻기 위해 사용하는 호출을 GetClassLongW (HWND, GCL_HICON)는, 64 비트 윈도우에서 아이콘을 반환하지 않습니다. 그걸 위해 GetClassLongPtr이 필요하다고 생각하지만 JNA를 통해 액세스 할 수없는 것 같습니다. 내가 this C++ answer에서 이루어집니다 무엇을 복제하려하지만 난 2 (GetAncestor 등) 및 3을 얻기 위해 (STATE_SYSTEM_INVISIBLE)를 관리 할 수 ​​-

  3. 에서 Alt-탭 팝업에 따라, 올바른 창 목록 얻기 Java에서 구현 된 검사. 나는 제목이 공백 인 창을 제외시키는 가난한 대체품을 사용하고 있습니다. (일부 합법적 인 창을 무시합니다).

참고 : JNA와 플랫폼 항아리는이 예제를 실행하는 데 필요한 : 나는 해결 방법 내 목적을 위해 할 것입니다 것으로 나타났습니다

package test; 

import static test.WindowSwitcher.User32DLL.*; 
import static test.WindowSwitcher.Gdi32DLL.*; 
import com.sun.jna.Native; 
import com.sun.jna.Pointer; 
import com.sun.jna.platform.win32.WinDef.HWND; 
import java.awt.GridLayout; 
import java.awt.Image; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.image.BufferedImage; 
import java.util.Vector; 
import javax.swing.ImageIcon; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.WindowConstants; 

public class WindowSwitcher 
{ 
    public static void main(String args[]) 
    { 
     JFrame JF = new JFrame(); 
     JPanel JP = new JPanel(new GridLayout(0, 1)); 

     JF.getContentPane().add(JP); 

     Vector<WindowInfo> V = getActiveWindows(); 
     for (int i = 0; i < V.size(); i++) 
     { 
      final WindowInfo WI = V.elementAt(i); 
      JButton JB = new JButton(WI.title); 

      if(WI.image != null) 
      { 
       JB.setIcon(new ImageIcon(WI.image)); 
      } 

      JB.addActionListener(new ActionListener() 
      { 
       @Override 
       public void actionPerformed(ActionEvent e) 
       { 
        SetForegroundWindow(WI.hWnd); 
       } 
      }); 

      JP.add(JB); 
     } 

     JF.setSize(600,50+V.size()*64); 
     JF.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     JF.setAlwaysOnTop(true); 
     JF.setFocusableWindowState(false); 
     JF.setVisible(true); 
    } 

    public static Vector<WindowInfo> getActiveWindows() 
    { 
     final Vector<WindowInfo> V = new Vector(); 

     EnumWindows(new WNDENUMPROC() 
     { 
      public boolean callback(Pointer hWndPointer, Pointer userData) 
      { 
       HWND hWnd = new HWND(hWndPointer); 

       // Make sure the window is visible 
       if(IsWindowVisible(hWndPointer)) 
       { 
        int GWL_EXSTYLE = -20; 
        long WS_EX_TOOLWINDOW = 0x00000080L; 

        // Make sure this is not a tool window 
        if((GetWindowLongW(hWndPointer, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) == 0) 
        { 
         // Get the title bar text for the window 
         char[] windowText = new char[512]; 
         GetWindowTextW(hWnd, windowText, windowText.length); 
         String wText = Native.toString(windowText); 

         // Make sure the text is not null or blank 
         if(!(wText == null || wText.trim().equals(""))) 
         { 
          // Get the icon image for the window (if available) 
          Image image = getImageForWindow(hWnd, wText); 

          // This window is a valid taskbar button, add a WindowInfo object to the return vector 
          V.add(new WindowInfo(wText, hWnd, image)); 
         } 
        } 
       } 

       return true; 
      } 
     }, null); 

     return V; 
    } 

    public static Image getImageForWindow(HWND hWnd, String wText) 
    { 
     // Get an image from the icon for this window 
     int hicon = GetClassLongW(hWnd, GCL_HICON); 

     if(hicon == 0) 
      return null; 

     Pointer hIcon = new Pointer(hicon); 

     int width = 64; 
     int height = 64; 
     BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
     draw(image, hIcon, DI_NORMAL); 
     BufferedImage mask = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
     draw(mask, hIcon, DI_MASK); 
     applyMask(image, mask); 

     return image; 
    } 
    public static void draw(BufferedImage image, Pointer hIcon, int diFlags) 
    { 
     int width = image.getWidth(); 
     int height = image.getHeight(); 

     Pointer hdc = CreateCompatibleDC(Pointer.NULL); 
     Pointer bitmap = CreateCompatibleBitmap(hdc, width, height); 

     SelectObject(hdc, bitmap); 
     DrawIconEx(hdc, 0, 0, hIcon, width, height, 0, Pointer.NULL, diFlags); 

     for (int x = 0; x < width; x++) 
     { 
      for (int y = 0; y < width; y++) 
      { 
       int rgb = GetPixel(hdc, x, y); 
       image.setRGB(x, y, rgb); 
      } 
     } 

     DeleteObject(bitmap); 
     DeleteDC(hdc); 
    } 
    private static void applyMask(BufferedImage image, 
      BufferedImage mask) 
    { 
     int width = image.getWidth(); 
     int height = image.getHeight(); 
     for (int x = 0; x < width; x++) 
     { 
      for (int y = 0; y < height; y++) 
      { 
       int masked = mask.getRGB(x, y); 
       if ((masked & 0x00FFFFFF) == 0) 
       { 
        int rgb = image.getRGB(x, y); 
        rgb = 0xFF000000 | rgb; 
        image.setRGB(x, y, rgb); 
       } 
      } 
     } 
    } 

    static class User32DLL 
    { 
     static 
     { 
      Native.register("user32"); 
     } 

     public static native int GetWindowTextW(HWND hWnd, char[] lpString, int nMaxCount); 

     public static native boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg); 

     public static interface WNDENUMPROC extends com.sun.jna.win32.StdCallLibrary.StdCallCallback 
     { 
      boolean callback(Pointer hWnd, Pointer arg); 
     } 

     public static native boolean IsWindowVisible(Pointer hWnd); 

     public static native boolean SetForegroundWindow(HWND hWnd); 

     public static native int GetWindowLongW(Pointer hWnd, int nIndex); 

     public static int GCL_HICON = -14; 
     public static int GCL_HICONSM = -34; 
     public static native int GetClassLongW(HWND hWnd, int nIndex); 

     /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */ 
     public static final int DI_COMPAT = 4; 
     public static final int DI_DEFAULTSIZE = 8; 
     public static final int DI_IMAGE = 2; 
     public static final int DI_MASK = 1; 
     public static final int DI_NORMAL = 3; 
     public static final int DI_APPBANDING = 1; 

     /** http://msdn.microsoft.com/en-us/library/ms648065(VS.85).aspx */ 
     public static native boolean DrawIconEx(Pointer hdc, int xLeft, 
       int yTop, Pointer hIcon, int cxWidth, int cyWidth, 
       int istepIfAniCur, Pointer hbrFlickerFreeDraw, 
       int diFlags); 
    } 

    static class Gdi32DLL 
    { 
     static 
     { 
      Native.register("gdi32"); 
     } 

     /** http://msdn.microsoft.com/en-us/library/dd183489(VS.85).aspx */ 
     public static native Pointer CreateCompatibleDC(Pointer hdc); 

     /** http://msdn.microsoft.com/en-us/library/dd183488(VS.85).aspx */ 
     public static native Pointer CreateCompatibleBitmap(Pointer hdc, int nWidth, int nHeight); 

     /** http://msdn.microsoft.com/en-us/library/dd162957(VS.85).aspx */ 
     public static native Pointer SelectObject(Pointer hdc, Pointer hgdiobj); 

     /** http://msdn.microsoft.com/en-us/library/dd145078(VS.85).aspx */ 
     public static native int SetPixel(Pointer hdc, int X, int Y, int crColor); 

     /** http://msdn.microsoft.com/en-us/library/dd144909(VS.85).aspx */ 
     public static native int GetPixel(Pointer hdc, int nXPos, int nYPos); 

     /** http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx */ 
     public static native boolean DeleteObject(Pointer hObject); 

     /** http://msdn.microsoft.com/en-us/library/dd183533(VS.85).aspx */ 
     public static native boolean DeleteDC(Pointer hdc); 
    } 
} 
class WindowInfo 
{ 
    String title; 
    HWND hWnd; 
    Image image; 

    public WindowInfo(String title, HWND hWnd, Image image) 
    { 
     this.title = title; 
     this.hWnd = hWnd; 
     this.image = image; 
    } 
} 
+1

1)'HICON.fromNative는()'* * 당신이 찾고있는하지 무엇입니까 . 네이티브 포인터를 Java HICON 인스턴스로 변환합니다. – technomage

+1

2)'GetClassLongPtr()'이 현재 사용중인 인터페이스 매핑에 없다면, 매핑을 확장하여 누락 된 함수를 추가 할 수 있습니다. – technomage

+1

3) Java에서 작동하도록 다른 호출을하면 일부 다른 (부분) 솔루션을 해킹하는 것보다 쉽습니다. JNA가 매핑을 제공하지 않는다면 적절한 w32 DLL에 대한 기존 매핑을 선택하거나 새로운 매핑을 확장합니다. – technomage

답변

1

. 첫 번째 시도보다 훨씬 간단합니다! Sun.awt.shell.ShellFolder를 사용하여 아이콘을 가져오고 있습니다. 불행히도 문서화되지 않은/지원되지 않는 클래스는 향후 Java 릴리스에서 제거 될 수 있습니다. FileSystemView를 사용하여 아이콘을 가져 오는 또 다른 방법이 있지만 반환 된 아이콘이 내 용도에 비해 너무 작습니다 (아래 예에서 주석 처리되었습니다 - getImageForWindowIcon 메서드).

이 해결 방법은 this SO answer by aleroot을 기반으로합니다. 프로세스 파일 경로 (다른 창 세부 정보가있는 WindowInfo 개체에 저장 한 창을 여는 데 사용 된 EXE 파일)을 얻은 다음 ShellFolder를 사용하여 해당 파일과 관련된 아이콘을 가져옵니다. 참고 :이 아이콘이 항상 올바른 것은 아닙니다 (예 : Netbeans 프로세스를 실행하는 데 사용되는 파일이 java.exe이므로 Netbeans가 아닌 Java 아이콘이 표시됨). 그래도 대부분의 경우 잘 작동합니다!

해결 방법은 위의 1 & 2 번 질문을 해결하지만 더 좋은 해결책이 있으면 알려 주시기 바랍니다. 나는 질문 3으로 어디에도 가지 않았지만 지금 가지고있는 창 목록은해야 할 일이다.

내 업데이트 된 코드는 다음과 같습니다. 참고 : JNA와 플랫폼 항아리이 예를 실행하는 데 필요한 :

package test; 

import static test.WindowSwitcher.User32DLL.*; 
import static test.WindowSwitcher.Kernel32.*; 
import static test.WindowSwitcher.Psapi.*; 
import com.sun.jna.Native; 
import com.sun.jna.Pointer; 
import com.sun.jna.platform.win32.WinDef.HWND; 
import com.sun.jna.ptr.PointerByReference; 
import java.awt.GridLayout; 
import java.awt.Image; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.io.File; 
import java.util.Vector; 
import javax.swing.ImageIcon; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.WindowConstants; 
import sun.awt.shell.ShellFolder; 

public class WindowSwitcher 
{ 
    public static void main(String args[]) 
    { 
     new WindowSwitcher(); 
    } 
    public WindowSwitcher() 
    { 
     JFrame JF = new JFrame("Window Switcher"); 
     JPanel JP = new JPanel(new GridLayout(0, 1)); 

     JF.getContentPane().add(JP); 

     Vector<WindowInfo> V = getActiveWindows(); 
     for (int i = 0; i < V.size(); i++) 
     { 
      final WindowInfo WI = V.elementAt(i); 
      JButton JB = new JButton(WI.title); 

      if(WI.image != null) 
      { 
       JB.setIcon(new ImageIcon(WI.image)); 
      } 

      JB.addActionListener(new ActionListener() 
      { 
       @Override 
       public void actionPerformed(ActionEvent e) 
       { 
        SetForegroundWindow(WI.hWnd); 
       } 
      }); 

      JP.add(JB); 
     } 

     JF.setSize(600,50+V.size()*64); 
     JF.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     JF.setAlwaysOnTop(true); 
     JF.setFocusableWindowState(false); 
     JF.setVisible(true); 
    } 

    private Vector<WindowInfo> getActiveWindows() 
    { 
     final Vector<WindowInfo> V = new Vector(); 

     EnumWindows(new WNDENUMPROC() 
     { 
      public boolean callback(Pointer hWndPointer, Pointer userData) 
      { 
       HWND hWnd = new HWND(hWndPointer); 

       // Make sure the window is visible 
       if(IsWindowVisible(hWndPointer)) 
       { 
        int GWL_EXSTYLE = -20; 
        long WS_EX_TOOLWINDOW = 0x00000080L; 

        // Make sure this is not a tool window 
        if((GetWindowLongW(hWndPointer, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) == 0) 
        { 
         // Get the title bar text for the window (and other info) 
         WindowInfo info = getWindowTitleAndProcessDetails(hWnd); 

         // Make sure the text is not null or blank 
         if(!(info.title == null || info.title.trim().equals(""))) 
         { 
          // Get the icon image for the window (if available) 
          info.image = getImageForWindow(info); 

          // This window is a valid taskbar button, add a WindowInfo object to the return vector 
          V.add(info); 
         } 
        } 
       } 

       return true; 
      } 
     }, null); 

     return V; 
    } 

    private static final int MAX_TITLE_LENGTH = 1024; 
    private WindowInfo getWindowTitleAndProcessDetails(HWND hWnd) 
    { 
     if(hWnd == null) 
      return null; 

     char[] buffer = new char[MAX_TITLE_LENGTH * 2]; 
     GetWindowTextW(hWnd, buffer, MAX_TITLE_LENGTH); 
     String title = Native.toString(buffer); 

     PointerByReference pointer = new PointerByReference(); 
     GetWindowThreadProcessId(hWnd, pointer); //GetForegroundWindow() 
     Pointer process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pointer.getValue()); 
     GetModuleBaseNameW(process, null, buffer, MAX_TITLE_LENGTH); 
     String Sprocess = Native.toString(buffer); 
     GetModuleFileNameExW(process, null, buffer, MAX_TITLE_LENGTH); 
     String SprocessFilePath = Native.toString(buffer); 

     return new WindowInfo(title, Sprocess, SprocessFilePath, hWnd, null); 
    } 

    private Image getImageForWindow(WindowInfo info) 
    { 
     if(info.processFilePath == null || info.processFilePath.trim().equals("")) 
      return null; 

     try 
     { 
      File f = new File(info.processFilePath); 

      if(f.exists()) 
      { 
       // https://stackoverflow.com/questions/10693171/how-to-get-the-icon-of-another-application 
       // http://www.rgagnon.com/javadetails/java-0439.html 
       ShellFolder sf = ShellFolder.getShellFolder(f); 
       if(sf != null) 
        return sf.getIcon(true); 

       // Image returned using this method is too small! 
       //Icon icon = FileSystemView.getFileSystemView().getSystemIcon(f); 
      } 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    static class Psapi 
    { 
     static 
     { 
      Native.register("psapi"); 
     } 

     public static native int GetModuleBaseNameW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size); 

     public static native int GetModuleFileNameExW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size); 
    } 

    static class Kernel32 
    { 

     static 
     { 
      Native.register("kernel32"); 
     } 
     public static int PROCESS_QUERY_INFORMATION = 0x0400; 
     public static int PROCESS_VM_READ = 0x0010; 

     public static native Pointer OpenProcess(int dwDesiredAccess, boolean bInheritHandle, Pointer pointer); 
    } 

    static class User32DLL 
    { 
     static 
     { 
      Native.register("user32"); 
     } 

     public static native int GetWindowThreadProcessId(HWND hWnd, PointerByReference pref); 

     public static native int GetWindowTextW(HWND hWnd, char[] lpString, int nMaxCount); 

     public static native boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg); 

     public static interface WNDENUMPROC extends com.sun.jna.win32.StdCallLibrary.StdCallCallback 
     { 
      boolean callback(Pointer hWnd, Pointer arg); 
     } 

     public static native boolean IsWindowVisible(Pointer hWnd); 

     public static native boolean SetForegroundWindow(HWND hWnd); 

     public static native int GetWindowLongW(Pointer hWnd, int nIndex); 
    } 
} 
class WindowInfo 
{ 
    String title, process, processFilePath; 
    HWND hWnd; 
    Image image; 

    public WindowInfo(String title, String process, String processFilePath, HWND hWnd, Image image) 
    { 
     this.title = title; 
     this.process = process; 
     this.processFilePath = processFilePath; 
     this.hWnd = hWnd; 
     this.image = image; 
    } 
} 
0

추악한 방법

ShellFolder sf = ShellFolder.getShellFolder(new File(".")); 
Method m = sf.getClass().getDeclaredMethod("makeIcon", Long.TYPE, Boolean.TYPE); 
m.setAccessible(true); 
Image image = (Image) m.invoke(null, Long.valueOf(iconHandle), Boolean.TRUE); 
+0

그 작품. 하지만 개인 클래스 Win32ShellFolder2.makeIcon (긴 hIcon, 부울 getLargeIcon)의 개인 메서드를 사용합니다. – igoru