JDK-8320692 : Null icon returned for .exe without custom icon
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 21,22
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows
  • CPU: x86_64
  • Submitted: 2023-11-22
  • Updated: 2024-06-05
  • Resolved: 2024-01-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 17 JDK 21 JDK 22 JDK 23
17.0.13-oracleFixed 21.0.4-oracleFixed 22.0.2Fixed 23 b07Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
The Open file dialog hangs when selecting a folder that contains .exe files with the default Windows exe icon.

Someone has already reported this bug (https://bugs.java.com/bugdatabase/view_bug?bug_id=8297529​), and it was marked as resolved. However, when I download the latest JDK version (jdk 21.0.1), the issue still persists. The root cause is that the 'newIcon' variable becomes null in the 'getIcon' function of the 'Win32ShellFolder2.class.'

The NullPointerException occurs as follows:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "java.awt.Image.getWidth(java.awt.image.ImageObserver)" because "retVal" is null
	at java.desktop/sun.awt.shell.Win32ShellFolder2$MultiResolutionIconImage.getResolutionVariant(Win32ShellFolder2.java:1459)
	at java.desktop/sun.awt.shell.Win32ShellFolder2$MultiResolutionIconImage.getBaseImage(Win32ShellFolder2.java:1438)
	at java.desktop/java.awt.image.AbstractMultiResolutionImage.getProperty(AbstractMultiResolutionImage.java:140)
	at java.desktop/javax.swing.ImageIcon.<init>(ImageIcon.java:255)
	at java.desktop/javax.swing.ImageIcon.<init>(ImageIcon.java:241)
	at java.desktop/javax.swing.filechooser.FileSystemView.getSystemIcon(FileSystemView.java:252)
	at java.desktop/com.sun.java.swing.plaf.windows.WindowsFileChooserUI$WindowsFileView.getIcon(WindowsFileChooserUI.java:1352)
	at java.desktop/javax.swing.JFileChooser.getIcon(JFileChooser.java:1613)
	at java.desktop/sun.swing.FilePane$FileRenderer.getListCellRendererComponent(FilePane.java:1635)
	at java.desktop/javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1444)
	at java.desktop/javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1394)
	at java.desktop/javax.swing.plaf.basic.BasicListUI.paintImpl(BasicListUI.java:330)
	at java.desktop/javax.swing.plaf.basic.BasicListUI.paint(BasicListUI.java:306)
	at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
	at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:852)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128)
	at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5318)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedFPScales(RepaintManager.java:1720)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1629)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1569)
	at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1336)
	at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5266)
	at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5076)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:878)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:861)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:861)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:834)
	at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:784)
	at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1897)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
	at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191)
	at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236)
	at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:319)
	at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234)
	at java.desktop/java.awt.Dialog.show(Dialog.java:1079)
	at java.desktop/javax.swing.JFileChooser.showDialog(JFileChooser.java:771)
	at java.desktop/javax.swing.JFileChooser.showOpenDialog(JFileChooser.java:668)
	at demo.showOpenFileDialog(demo.java:90)
	at demo.access$0(demo.java:51)
	at demo$1.actionPerformed(demo.java:28)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2314)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:407)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6621)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3398)
	at java.desktop/java.awt.Component.processEvent(Component.java:6386)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4996)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Open the Open file dialog.
2. Navigate to a folder containing .exe files with the default Windows exe icon.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The Open file dialog should not hang, and all files in the specified folder should load and be selectable within the dialog.
ACTUAL -
Throws NullPointerException 

---------- BEGIN SOURCE ----------
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter;

public class demo extends JFrame {

	private JButton buttonBrowse;
	 
    public demo()  {
        super("Demo File Type Filter");
        setLayout(new FlowLayout());
        buttonBrowse = new JButton("Browse...");
 
        buttonBrowse.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                showOpenFileDialog();
            }
        });
 
        getContentPane().add(buttonBrowse);
        setSize(300, 100);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }
 
    public static void main(String[] args) {
        try {
        	UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) { }
 
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new demo();
            }
        });
    }
 
    private void showOpenFileDialog() {
    // Contain some exe files with the default Windows exe icon
    	File fileTest = new File("D:/Downloads/Test");
    	
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setCurrentDirectory(fileTest);
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
 
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("EXE File","exe"));

        int result = fileChooser.showOpenDialog(this);
 
        if (result == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            System.out.println("Selected file: " + selectedFile.getAbsolutePath());
        }
    }
}
---------- END SOURCE ----------


Comments
[jdk17u-fix-request] Approval Request from Martin Should get backported for parity with 17.0.12-oracle. Applies almost cleanly. Tier 1-4 have passed.
04-06-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/2527 Date: 2024-06-03 20:34:19 +0000
03-06-2024

[jdk21u-fix-request] Approval Request from Martin Should get backported for parity with 21.0.4-oracle. Applies cleanly and the tests have passed.
29-05-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk21u-dev/pull/614 Date: 2024-05-28 20:47:42 +0000
28-05-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk22u/pull/167 Date: 2024-04-25 16:30:13 +0000
25-04-2024

Fix request: 22u It resolves a regression introduced in Java 21 by JDK-8293862. Clean backport. Low risk. Pull request: https://github.com/openjdk/jdk22u/pull/167
25-04-2024

Changeset: 6212264c Author: Alexander Zuev <kizune@openjdk.org> Date: 2024-01-24 22:10:28 +0000 URL: https://git.openjdk.org/jdk/commit/6212264cc6fe428e8d15b7f33e2979c081e432d7
24-01-2024

I totally forgot about the assertions added JDK-8293862. If you run the test case above with '-ea -esa' options, you'll get the following exception: Exception in thread "main" java.lang.AssertionError: There are null icons in the MRI variants map at java.desktop/sun.awt.shell.Win32ShellFolder2$MultiResolutionIconImage.<init>(Win32ShellFolder2.java:1414) at java.desktop/sun.awt.shell.Win32ShellFolder2.lambda$getIcon$0(Win32ShellFolder2.java:1201) at java.desktop/sun.awt.shell.Win32ShellFolderManager2$ComInvoker.invoke(Win32ShellFolderManager2.java:630) at java.desktop/sun.awt.shell.ShellFolder.invoke(ShellFolder.java:532) at java.desktop/sun.awt.shell.ShellFolder.invoke(ShellFolder.java:518) at java.desktop/sun.awt.shell.Win32ShellFolder2.getIcon(Win32ShellFolder2.java:1154) at java.desktop/sun.awt.shell.Win32ShellFolder2$15.call(Win32ShellFolder2.java:1128) at java.desktop/sun.awt.shell.Win32ShellFolder2$15.call(Win32ShellFolder2.java:1067) which clearly shows where the problem occurs.
18-01-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/17475 Date: 2024-01-18 00:47:00 +0000
18-01-2024

[~arapte] The problem is reproducible with 21+35 which is GA of 21; it's also reproducible with 21+7 where JDK-8293862 was fixed but it's not reproducible in the previous build 21+6. This means this bug is a regression from JDK-8293862. In 21+6, .exe without icons are displayed with a generic Swing icon. No NPE is thrown, yet the displayed icon should be the default icon displayed by Windows. Since JDK-8293862 is not backported, previous releases don't throw NPE. At the same time, JDK-8293862 is a regression from JDK-8182043 which is resolved in 17, see this part of the analysis [1]: “This problem has existed since JDK-8182043, yet it went unnoticed because getResolutionVariant just returned a null. However, after JDK-8282526 was integrated, the result is used, which results in NullPointerException.” For this reason, the fixes for JDK-8282526 and JDK-8293862 are to be backported to 17u. As soon as it's done, the problem will be reproducible. Therefore, the backports must include the fix for this bug, and backporting shouldn't start until this bug is fixed not to introduce known regressions into 17u code base. [1] https://bugs.openjdk.org/browse/JDK-8293862?focusedId=14551415&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14551415
08-01-2024

I could reproduce with jdk 21.0.1, with SwingSet2 always. but not with other/earlier JDK releases.
08-01-2024

Although the exception comes from the same code line, the root cause here is different than in JDK-8293862. In that bug, the problem was that a valid icon handle had been interpreted as invalid, thus a valid icon wasn't extracted. In this case, there's no icon to extract; I expect the shell to fallback to the default app icon automatically, but it doesn't happen, so we have to provide the fallback ourselves. It is a new case that hasn't been encountered before.
04-01-2024

I attached Hello.exe, a small Win32 app which displays "Hello World!", and created a test case for this bug: https://github.com/aivanov-jdk/jdk/commit/87144a46657b06600c710168a669800439ed8adb
04-01-2024

I may be able to look into it, I still have all the debug code for JDK-8293862, which should help finding the root cause and resolving the problem.
03-01-2024

When I was writing my reply to the above email, I realised the problem could not be limited to .exe files only: “The problem is likely applicable to other file types which have per-instance icons, however, these are uncommon with the exception of exe files. The suggested fix won't work, there should be an icon but we have to handle the fallback gracefully.” https://github.com/openjdk/jdk/pull/12010#issuecomment-1874358079 https://mail.openjdk.org/pipermail/client-libs-dev/2024-January/016760.html
02-01-2024

This is reproducible if you navigate to a folder which contains .exe files which provide no custom icon, I get NPE if I navigate to C:\Windows. The steps below are taken from https://mail.openjdk.org/pipermail/client-libs-dev/2024-January/016756.html https://github.com/openjdk/jdk/pull/12010#issuecomment-1873980572 Steps to reproduce: 1. Start SwingSet2. 2. Change Look & Feel to Windows Style Look & Feel. 3. Click JFileChooser Demo button (the fifth button). 4. Click Show Plain JFileChooser. 5. Navigate to C:\Windows. This problem is closely related to JDK-8293862. I expect Windows returns the default icon for the exe if a custom icon isn't available; yet in this case no icon is returned. This situation needs to be handled.
02-01-2024

According to the evaluation in JDK-8293862: “This problem has existed since JDK-8182043, yet it went unnoticed because getResolutionVariant just returned a null. However, after JDK-8282526 was integrated, the result is used, which results in NullPointerException.” This bug also applies to 17. The NPE isn't reproducible in 17 because JDK-8282526 hasn't been backported to 17u yet.
02-01-2024

Additional Information from submitter =============================== <attached short clipping> This is a short clip about the issue I encountered on JDK 21. This issue does not occur if I use JDK 17 or below; it only happens when I use JDK 21. In the clip, I tested in two scenarios. In scenario 1, if the folder contains files with icons that are not the default Windows exe icon, everything works normally. However, in scenario 2, if the folder contains files with the default Windows exe icon, the JFileChooser's open file dialog hangs and returns a NullPointerException.
27-11-2023

Checked with attached testcase in Windows 11, issue is not reproducible (checked with folder containing exe file ) Test Result =========== 8u391 : Pass 11.0.21: Pass 17.0.9: Pass 21.0.1: Pass 22ea25: Pass Mail to submitter =============== Please share short clipping of the issue.
24-11-2023