Java – 使用JNA的Windows任务栏 – 如何将窗口图标(HICON)转换为Java图像?

我正在尝试将一个应用程序切换器添加到我正在处理的更大的项目中。 它需要在Windows XP / Vista / 7/8上运行。 我正在使用Java 1.7。 以下是我创build的示例应用程序,用于演示我遇到的一些问题。 我对JNA很新。

非常感谢“气垫船充满了鳗鱼” 这个答案 (和许多其他!),这是构成testing应用的基础。

这是我的问题:

  1. 图像绘制 – 我从窗口图标获得的图像正在绘制成黑色和白色。 我从McDowell的这个答案修改了getImageForWindow中的代码(谢谢!)。 有没有更好的方法将HICON对象转换为java.awt.Image? 我注意到在com.sun.jna.platform.win32.W32API.HICON中有一个名为'fromNative'的方法,但我不知道如何使用它。

  2. 获取图标 – 我用来获取图标句柄的函数GetClassLongW(hWnd,GCL_HICON)不会从64位窗口返回图标。 我想我需要GetClassLongPtr,但我似乎无法通过JNA访问它。

  3. 根据Alt-Tabpopup窗口获取正确的窗口列表 – 我试图复制在这个C ++答案中做了什么,但我无法设法获得在Java中实现的第二个(GetAncestor等)和第三个(STATE_SYSTEM_INVISIBLE)检查。 我正在使用一个可怜的替代品来排除标题为空的窗口(忽略了一些合法的窗口)。

注意:运行此示例需要JNA和Platform Jars:

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

我找到了一个解决办法,将为我的目的。 这比我的第一次尝试更直接! 我现在正在使用sun.awt.shell.ShellFolder获取图标,不幸的是这是一个未公开的/不受支持的类,可能在将来的Java版本中被删除。 还有一种方法可以使用FileSystemView来获取图标,但是返回的图标对于我的目的来说太小了(在我的示例中,它被注释掉了 – getImageForWindowIcon方法)。

这个解决方法是基于这个由aleroot回答的 。 我得到了进程文件路径(用于打开窗口的exe文件,我将其存储在具有其他窗口细节的WindowInfo对象中),然后使用ShellFolder获取与该文件关联的图标。 注意:这并不总是给出正确的图标(例如,用来运行Netbeans进程的文件是java.exe,所以你得到的是Java图标,而不是Netbeans的!)。 大部分虽然,它运作良好!

解决方法解决了上面的问题1和2,但如果有人有更好的解决方案,请让我知道。 我没有得到问题3的任何地方,但我现在必须做的窗口清单。

这里是我更新的代码… 注意:运行这个例子需要JNA和Platform jars:

  package test; import static test.WindowSwitcher.User32DLL.*; import static test.WindowSwitcher.coreel32.*; 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.Actionlistner; 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.addActionlistner(new Actionlistner() { @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()); GetmoduleeBaseNameW(process, null, buffer, MAX_TITLE_LENGTH); String Sprocess = Native.toString(buffer); GetmoduleeFileNameExW(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 GetmoduleeBaseNameW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size); public static native int GetmoduleeFileNameExW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size); } static class coreel32 { 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; } } 

丑陋的方法

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