Java - 使用JNA的Windows任务栏 - 如何将窗口图标(HICON)转换为Java图像? [英] Java - Windows taskbar using JNA - How do I convert window icons (HICON) to java images?

查看:651
本文介绍了Java - 使用JNA的Windows任务栏 - 如何将窗口图标(HICON)转换为Java图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to add an application-switcher to a bigger project I'm working on. It needs to work on Windows XP/Vista/7/8. I'm using Java 1.7. Below is a sample application I created to demonstrate some of the problems I'm having. I'm very new to JNA.

非常感谢气垫船充满鳗鱼的这个答案(以及其他许多人!)构成了测试申请的基础。

Thanks very much to 'Hovercraft Full of Eels' for this answer (and many others!) which forms the basis for the test application.

以下是我的问题:


  1. 图像绘制 - 我从窗口图标中获取的图像以黑白方式绘制。我通过McDowell的这个答案修改了getImageForWindow中的代码(谢谢!)。有没有更好的方法将HICON对象转换为java.awt.Image?我注意到com.sun.jna.platform.win32.W32API.HICON中有一个名为'fromNative'的方法,但我无法弄清楚如何使用它。

  1. Image drawing - The images that I get from the window icons are being drawn in black and white. I modified the code in getImageForWindow from this answer by McDowell (Thanks!). Is there a better way to convert HICON objects to java.awt.Image? I notice there's a method called 'fromNative' in com.sun.jna.platform.win32.W32API.HICON but I can't figure out how to use it.

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

Getting the icons - The call I use to get an icon handle, GetClassLongW(hWnd, GCL_HICON), doesn't return icons from 64bit windows. I think I need GetClassLongPtr for that but I can't seem to access it via JNA.

获取正确的窗口列表,按照Alt -tab popup - 我试图复制此C ++答案中的操作但我无法设法获得用Java实现的第二个(GetAncestor等)和第三个(STATE_SYSTEM_INVISIBLE)检查。我正在使用一个糟糕的替代品,它排除标题为空白的窗口(忽略了一些合法的窗口)。

Getting the correct window list, as per the Alt-tab popup - I tried to replicate what is done in this C++ answer but I couldn't manage to get the 2nd (GetAncestor, etc) and 3rd (STATE_SYSTEM_INVISIBLE) checks implemented in Java. I'm using a poor substitute which is to exclude windows where the title is blank (which ignores some legitimate windows).

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

Note: JNA and Platform jars are required to run this example:

    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方法)。

I've found a work-around that will do for my purposes. It's much more straightforward than my first attempt! I am now getting the icons using sun.awt.shell.ShellFolder which unfortunately is an undocumented/unsupported class that may be removed in future Java releases. There is another method to get the icon using FileSystemView but the icon returned is too small for my purposes (it's commented out in my example below - getImageForWindowIcon method).

这项工作-around基于 aleroot的这个SO答案。我得到进程文件路径(用于打开窗口的exe文件,我存储在WindowInfo对象中,其他窗口详细信息),然后我使用ShellFolder获取与该文件关联的图标。 注意:这并不总是给出正确的图标(例如,用于运行Netbeans进程的文件是java.exe,因此您获得了Java图标,而不是Netbeans图标!)。但在大多数情况下,它运作良好!

This work-around is based on this SO answer by aleroot. I get the process file path (the exe file that was used to open the window, which I store in a WindowInfo object with other window details), then I use ShellFolder to get the icon associated with that file. Note: This does not always give the right icon (e.g. the file used to run the Netbeans process is java.exe so you get the Java icon, not the Netbeans one!). For the most part though, it works well!

解决方案解决问题1&以上2,但如果有人有任何更好的解决方案,请告诉我。我没有得到问题3的任何地方,但我现在必须做的窗口列表。

The work-around solves questions 1 & 2 above but if anybody has any better solutions, please let me know. I didn't get anywhere with question 3 but the window list I have will have to do for now.

这是我更新的代码...
注意:运行此示例需要JNA和Platform jar:

Here is my updated code... Note: JNA and Platform jars are required to run this example:

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

这篇关于Java - 使用JNA的Windows任务栏 - 如何将窗口图标(HICON)转换为Java图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆